Skip to content

Commit 94abb0d

Browse files
committed
copy elision: if-bool with scalar types
```zig export fn entry() void { var c = true; var y: i32 = if (c) 1234 else 5678; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %c = alloca i1, align 1 %y = alloca i32, align 4 store i1 true, i1* %c, align 1, !dbg !51 call void @llvm.dbg.declare(metadata i1* %c, metadata !45, metadata !DIExpression()), !dbg !52 %0 = load i1, i1* %c, align 1, !dbg !53 br i1 %0, label %Then, label %Else, !dbg !53 Then: ; preds = %Entry store i32 1234, i32* %y, align 4, !dbg !54 br label %EndIf, !dbg !55 Else: ; preds = %Entry store i32 5678, i32* %y, align 4, !dbg !56 br label %EndIf, !dbg !55 EndIf: ; preds = %Else, %Then call void @llvm.dbg.declare(metadata i32* %y, metadata !48, metadata !DIExpression()), !dbg !57 ret void, !dbg !58 } ```
1 parent d77b368 commit 94abb0d

File tree

4 files changed

+108
-5
lines changed

4 files changed

+108
-5
lines changed

src/all_types.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,6 +2187,7 @@ enum IrInstructionId {
21872187
IrInstructionIdAllocaGen,
21882188
IrInstructionIdAssertNonError,
21892189
IrInstructionIdErrorUnionFieldErrorSet,
2190+
IrInstructionIdFirstArgResultLoc,
21902191
};
21912192

21922193
struct IrInstruction {
@@ -3371,6 +3372,13 @@ struct IrInstructionAssertNonError {
33713372
IrInstruction *err_code;
33723373
};
33733374

3375+
struct IrInstructionFirstArgResultLoc {
3376+
IrInstruction base;
3377+
3378+
IrInstruction *prev_result_loc;
3379+
IrInstruction *fn_ref;
3380+
};
3381+
33743382
static const size_t slice_ptr_index = 0;
33753383
static const size_t slice_len_index = 1;
33763384

src/codegen.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5241,6 +5241,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
52415241
case IrInstructionIdDeclVarSrc:
52425242
case IrInstructionIdAllocaSrc:
52435243
case IrInstructionIdAllocaGen:
5244+
case IrInstructionIdFirstArgResultLoc:
52445245
zig_unreachable();
52455246

52465247
case IrInstructionIdDeclVarGen:

src/ir.cpp

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnionFieldE
907907
return IrInstructionIdErrorUnionFieldErrorSet;
908908
}
909909

910+
static constexpr IrInstructionId ir_instruction_id(IrInstructionFirstArgResultLoc *) {
911+
return IrInstructionIdFirstArgResultLoc;
912+
}
913+
910914
template<typename T>
911915
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
912916
T *special_instruction = allocate<T>(1);
@@ -2936,6 +2940,19 @@ static IrInstruction *ir_build_error_union_field_error_set(IrBuilder *irb, Scope
29362940
return &instruction->base;
29372941
}
29382942

2943+
static IrInstruction *ir_build_first_arg_result_loc(IrBuilder *irb, Scope *scope, AstNode *source_node,
2944+
IrInstruction *prev_result_loc, IrInstruction *fn_ref)
2945+
{
2946+
IrInstructionFirstArgResultLoc *instruction = ir_build_instruction<IrInstructionFirstArgResultLoc>(irb, scope, source_node);
2947+
instruction->prev_result_loc = prev_result_loc;
2948+
instruction->fn_ref = fn_ref;
2949+
2950+
ir_ref_instruction(prev_result_loc, irb->current_basic_block);
2951+
ir_ref_instruction(fn_ref, irb->current_basic_block);
2952+
2953+
return &instruction->base;
2954+
}
2955+
29392956
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
29402957
results[ReturnKindUnconditional] = 0;
29412958
results[ReturnKindError] = 0;
@@ -4969,16 +4986,31 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node
49694986
if (fn_ref == irb->codegen->invalid_instruction)
49704987
return fn_ref;
49714988

4989+
// In pass 1, we can't tell the difference between a function call with a single argument
4990+
// and an implicit cast. So if there is only one argument, we emit a special instruction.
49724991
size_t arg_count = node->data.fn_call_expr.params.length;
49734992
IrInstruction **args = allocate<IrInstruction*>(arg_count);
4993+
bool is_async = node->data.fn_call_expr.is_async;
4994+
if (arg_count == 1 && !is_async) {
4995+
AstNode *arg_node = node->data.fn_call_expr.params.at(0);
4996+
IrInstruction *arg_result_loc = ir_build_first_arg_result_loc(irb, scope, arg_node, result_loc, fn_ref);
4997+
args[0] = ir_gen_node(irb, arg_node, scope, LValNone, arg_result_loc);
4998+
if (args[0] == irb->codegen->invalid_instruction)
4999+
return irb->codegen->invalid_instruction;
5000+
// In the analysis, this call instruction will be a simple LoadPtr instruction if
5001+
// it turns out to be an implicit cast.
5002+
IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false,
5003+
FnInlineAuto, false, nullptr, nullptr, result_loc);
5004+
return ir_lval_wrap(irb, scope, fn_call, lval);
5005+
}
5006+
49745007
for (size_t i = 0; i < arg_count; i += 1) {
49755008
AstNode *arg_node = node->data.fn_call_expr.params.at(i);
49765009
args[i] = ir_gen_node(irb, arg_node, scope, LValNone, nullptr);
49775010
if (args[i] == irb->codegen->invalid_instruction)
49785011
return args[i];
49795012
}
49805013

4981-
bool is_async = node->data.fn_call_expr.is_async;
49825014
IrInstruction *async_allocator = nullptr;
49835015
if (is_async) {
49845016
if (node->data.fn_call_expr.async_allocator) {
@@ -13989,6 +14021,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call
1398914021
}
1399014022

1399114023
static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) {
14024+
Error err;
1399214025
IrInstruction *fn_ref = call_instruction->fn_ref->child;
1399314026
if (type_is_invalid(fn_ref->value.type))
1399414027
return ira->codegen->invalid_instruction;
@@ -14010,12 +14043,18 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC
1401014043
return ira->codegen->invalid_instruction;
1401114044
}
1401214045

14013-
IrInstruction *arg = call_instruction->args[0]->child;
14046+
// This is handled with the result location mechanism. So all we need to do here is
14047+
// a LoadPtr on the result location.
14048+
IrInstruction *result_loc = call_instruction->result_loc->child;
14049+
if (type_is_invalid(result_loc->value.type))
14050+
return ira->codegen->invalid_instruction;
14051+
if ((err = resolve_possible_alloca_inference(ira, result_loc, dest_type)))
14052+
return ira->codegen->invalid_instruction;
1401414053

14015-
IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, dest_type, arg);
14016-
if (type_is_invalid(cast_instruction->value.type))
14054+
IrInstruction *deref = ir_get_deref(ira, &call_instruction->base, result_loc);
14055+
if (type_is_invalid(deref->value.type))
1401714056
return ira->codegen->invalid_instruction;
14018-
return ir_finish_anal(ira, cast_instruction);
14057+
return ir_finish_anal(ira, deref);
1401914058
} else if (fn_ref->value.type->id == ZigTypeIdFn) {
1402014059
ZigFn *fn_table_entry = ir_resolve_fn(ira, fn_ref);
1402114060
if (fn_table_entry == nullptr)
@@ -21357,6 +21396,47 @@ static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructio
2135721396
return &result->base;
2135821397
}
2135921398

21399+
static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira,
21400+
IrInstructionFirstArgResultLoc *instruction)
21401+
{
21402+
Error err;
21403+
21404+
IrInstruction *fn_ref = instruction->fn_ref->child;
21405+
if (type_is_invalid(fn_ref->value.type))
21406+
return ira->codegen->invalid_instruction;
21407+
21408+
if (fn_ref->value.type->id == ZigTypeIdMetaType) {
21409+
// Result of this instruction should be the implicitly casted result location.
21410+
21411+
ZigType *dest_type = ir_resolve_type(ira, fn_ref);
21412+
if (type_is_invalid(dest_type))
21413+
return ira->codegen->invalid_instruction;
21414+
21415+
IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->prev_result_loc->child,
21416+
dest_type);
21417+
if (type_is_invalid(new_result_loc->value.type))
21418+
return ira->codegen->invalid_instruction;
21419+
return new_result_loc;
21420+
}
21421+
21422+
// Result of this instruction should be the result location for the first argument of the function call.
21423+
// This means it should be a stack allocation.
21424+
IrInstructionAllocaGen *result = ir_create_alloca_gen(&ira->new_irb, instruction->base.scope,
21425+
instruction->base.source_node, 0, "");
21426+
21427+
assert(fn_ref->value.type->id == ZigTypeIdFn);
21428+
ZigType *param_type = fn_ref->value.type->data.fn.fn_type_id.param_info[0].type;
21429+
if (type_is_invalid(param_type))
21430+
return ira->codegen->invalid_instruction;
21431+
if ((err = resolve_alloca_inference(ira, result, param_type)))
21432+
return ira->codegen->invalid_instruction;
21433+
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
21434+
if (fn_entry != nullptr) {
21435+
fn_entry->alloca_list.append(result);
21436+
}
21437+
return &result->base;
21438+
}
21439+
2136021440
static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
2136121441
switch (instruction->id) {
2136221442
case IrInstructionIdInvalid:
@@ -21650,6 +21730,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
2165021730
return ir_analyze_instruction_alloca(ira, (IrInstructionAllocaSrc *)instruction);
2165121731
case IrInstructionIdErrorUnionFieldErrorSet:
2165221732
return ir_analyze_instruction_error_union_field_error_set(ira, (IrInstructionErrorUnionFieldErrorSet *)instruction);
21733+
case IrInstructionIdFirstArgResultLoc:
21734+
return ir_analyze_instruction_first_arg_result_loc(ira, (IrInstructionFirstArgResultLoc *)instruction);
2165321735
case IrInstructionIdResultBytesToSlice:
2165421736
zig_panic("TODO");
2165521737
case IrInstructionIdResultSliceToBytes:
@@ -21887,6 +21969,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
2188721969
case IrInstructionIdAllocaSrc:
2188821970
case IrInstructionIdAllocaGen:
2188921971
case IrInstructionIdErrorUnionFieldErrorSet:
21972+
case IrInstructionIdFirstArgResultLoc:
2189021973
return false;
2189121974

2189221975
case IrInstructionIdLoadPtr:

src/ir_print.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,6 +1399,14 @@ static void ir_print_error_union_field_error_set(IrPrint *irp, IrInstructionErro
13991399
fprintf(irp->f, ")");
14001400
}
14011401

1402+
static void ir_print_first_arg_result_loc(IrPrint *irp, IrInstructionFirstArgResultLoc *instruction) {
1403+
fprintf(irp->f, "FirstArgResultLoc(prev_result=");
1404+
ir_print_other_instruction(irp, instruction->prev_result_loc);
1405+
fprintf(irp->f, ",fn=");
1406+
ir_print_other_instruction(irp, instruction->fn_ref);
1407+
fprintf(irp->f, ")");
1408+
}
1409+
14021410
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
14031411
ir_print_prefix(irp, instruction);
14041412
switch (instruction->id) {
@@ -1863,6 +1871,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
18631871
case IrInstructionIdErrorUnionFieldErrorSet:
18641872
ir_print_error_union_field_error_set(irp, (IrInstructionErrorUnionFieldErrorSet *)instruction);
18651873
break;
1874+
case IrInstructionIdFirstArgResultLoc:
1875+
ir_print_first_arg_result_loc(irp, (IrInstructionFirstArgResultLoc *)instruction);
1876+
break;
18661877
}
18671878
fprintf(irp->f, "\n");
18681879
}

0 commit comments

Comments
 (0)