IR: add ref instruction

This commit is contained in:
Andrew Kelley 2016-11-27 01:22:30 -05:00
parent bd4d4ee51e
commit d9329ed389
4 changed files with 103 additions and 16 deletions

View File

@ -1081,8 +1081,7 @@ struct FnTableEntry {
AstNode *fn_test_set_node; AstNode *fn_test_set_node;
AstNode *fn_static_eval_set_node; AstNode *fn_static_eval_set_node;
ZigList<IrInstructionCast *> cast_alloca_list; ZigList<IrInstruction *> alloca_list;
ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
ZigList<VariableTableEntry *> variable_list; ZigList<VariableTableEntry *> variable_list;
}; };
@ -1414,6 +1413,7 @@ enum IrInstructionId {
IrInstructionIdStaticEval, IrInstructionIdStaticEval,
IrInstructionIdImport, IrInstructionIdImport,
IrInstructionIdArrayLen, IrInstructionIdArrayLen,
IrInstructionIdRef,
}; };
struct IrInstruction { struct IrInstruction {
@ -1771,6 +1771,13 @@ struct IrInstructionArrayLen {
IrInstruction *array_value; IrInstruction *array_value;
}; };
struct IrInstructionRef {
IrInstruction base;
IrInstruction *value;
LLVMValueRef tmp_ptr;
};
enum LValPurpose { enum LValPurpose {
LValPurposeNone, LValPurposeNone,
LValPurposeAssign, LValPurposeAssign,

View File

@ -1661,6 +1661,16 @@ static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutable *executable, IrInstru
return phi; return phi;
} }
static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstructionRef *instruction) {
LLVMValueRef value = ir_llvm_value(g, instruction->value);
if (handle_is_ptr(instruction->value->type_entry)) {
return value;
} else {
LLVMBuildStore(g->builder, value, instruction->tmp_ptr);
return instruction->tmp_ptr;
}
}
static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) { static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) {
set_debug_source_node(g, instruction->source_node); set_debug_source_node(g, instruction->source_node);
@ -1724,6 +1734,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_switch_br(g, executable, (IrInstructionSwitchBr *)instruction); return ir_render_switch_br(g, executable, (IrInstructionSwitchBr *)instruction);
case IrInstructionIdPhi: case IrInstructionIdPhi:
return ir_render_phi(g, executable, (IrInstructionPhi *)instruction); return ir_render_phi(g, executable, (IrInstructionPhi *)instruction);
case IrInstructionIdRef:
return ir_render_ref(g, executable, (IrInstructionRef *)instruction);
case IrInstructionIdSwitchVar: case IrInstructionIdSwitchVar:
case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitList:
case IrInstructionIdContainerInitFields: case IrInstructionIdContainerInitFields:
@ -2317,17 +2329,20 @@ static void do_code_gen(CodeGen *g) {
clear_debug_source_node(g); clear_debug_source_node(g);
// allocate structs which are the result of casts // allocate temporary stack data
for (size_t cea_i = 0; cea_i < fn_table_entry->cast_alloca_list.length; cea_i += 1) { for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) {
IrInstructionCast *cast_instruction = fn_table_entry->cast_alloca_list.at(cea_i); IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i);
cast_instruction->tmp_ptr = LLVMBuildAlloca(g->builder, cast_instruction->base.type_entry->type_ref, ""); LLVMValueRef *slot;
} if (instruction->id == IrInstructionIdCast) {
IrInstructionCast *cast_instruction = (IrInstructionCast *)instruction;
// allocate structs which are struct value expressions slot = &cast_instruction->tmp_ptr;
for (size_t alloca_i = 0; alloca_i < fn_table_entry->struct_val_expr_alloca_list.length; alloca_i += 1) { } else if (instruction->id == IrInstructionIdRef) {
StructValExprCodeGen *struct_val_expr_node = fn_table_entry->struct_val_expr_alloca_list.at(alloca_i); IrInstructionRef *ref_instruction = (IrInstructionRef *)instruction;
struct_val_expr_node->ptr = LLVMBuildAlloca(g->builder, slot = &ref_instruction->tmp_ptr;
struct_val_expr_node->type_entry->type_ref, ""); } else {
zig_unreachable();
}
*slot = LLVMBuildAlloca(g->builder, instruction->type_entry->type_ref, "");
} }
// create debug variable declarations for variables and allocate all local variables // create debug variable declarations for variables and allocate all local variables

View File

@ -272,6 +272,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayLen *) {
return IrInstructionIdArrayLen; return IrInstructionIdArrayLen;
} }
static constexpr IrInstructionId ir_instruction_id(IrInstructionRef *) {
return IrInstructionIdRef;
}
template<typename T> template<typename T>
static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) { static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) {
T *special_instruction = allocate<T>(1); T *special_instruction = allocate<T>(1);
@ -1112,6 +1116,21 @@ static IrInstruction *ir_build_array_len_from(IrBuilder *irb, IrInstruction *old
return new_instruction; return new_instruction;
} }
static IrInstruction *ir_build_ref(IrBuilder *irb, AstNode *source_node, IrInstruction *value) {
IrInstructionRef *instruction = ir_build_instruction<IrInstructionRef>(irb, source_node);
instruction->value = value;
ir_ref_instruction(value);
return &instruction->base;
}
static IrInstruction *ir_build_ref_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) {
IrInstruction *new_instruction = ir_build_ref(irb, old_instruction->source_node, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, BlockContext *outer_block, static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, BlockContext *outer_block,
bool gen_error_defers, bool gen_maybe_defers) bool gen_error_defers, bool gen_maybe_defers)
{ {
@ -2493,6 +2512,17 @@ static IrInstruction *ir_gen_goto(IrBuilder *irb, AstNode *node) {
return ir_build_unreachable(irb, node); return ir_build_unreachable(irb, node);
} }
static IrInstruction *ir_lval_wrap(IrBuilder *irb, IrInstruction *value, LValPurpose lval) {
if (lval == LValPurposeNone)
return value;
if (value == irb->codegen->invalid_instruction)
return value;
// We needed a pointer to a value, but we got a value. So we create
// an instruction which just makes a const pointer of it.
return ir_build_ref(irb, value->source_node, value);
}
static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, BlockContext *block_context, static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, BlockContext *block_context,
LValPurpose lval) LValPurpose lval)
{ {
@ -2509,7 +2539,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, BlockContex
case NodeTypeSymbol: case NodeTypeSymbol:
return ir_gen_symbol(irb, node, lval); return ir_gen_symbol(irb, node, lval);
case NodeTypeFnCallExpr: case NodeTypeFnCallExpr:
return ir_gen_fn_call(irb, node); return ir_lval_wrap(irb, ir_gen_fn_call(irb, node), lval);
case NodeTypeIfBoolExpr: case NodeTypeIfBoolExpr:
return ir_gen_if_bool_expr(irb, node); return ir_gen_if_bool_expr(irb, node);
case NodeTypePrefixOpExpr: case NodeTypePrefixOpExpr:
@ -2898,8 +2928,7 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst
dest_type, value, cast_op); dest_type, value, cast_op);
result->type_entry = wanted_type; result->type_entry = wanted_type;
if (need_alloca && source_instr->source_node->block_context->fn_entry) { if (need_alloca && source_instr->source_node->block_context->fn_entry) {
IrInstructionCast *cast_instruction = (IrInstructionCast *)result; source_instr->source_node->block_context->fn_entry->alloca_list.append(result);
source_instr->source_node->block_context->fn_entry->cast_alloca_list.append(cast_instruction);
} }
return result; return result;
} }
@ -5700,6 +5729,31 @@ static TypeTableEntry *ir_analyze_instruction_array_len(IrAnalyze *ira,
} }
} }
static TypeTableEntry *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRef *ref_instruction) {
IrInstruction *value = ref_instruction->value->other;
if (value->type_entry->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
FnTableEntry *fn_entry = ref_instruction->base.source_node->block_context->fn_entry;
if (!fn_entry || value->static_value.special != ConstValSpecialRuntime) {
ConstExprValue *val = ir_resolve_const(ira, value);
if (!val)
return ira->codegen->builtin_types.entry_invalid;
return ir_analyze_const_ptr(ira, &ref_instruction->base, val, value->type_entry, false);
}
TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, value->type_entry, true);
if (handle_is_ptr(value->type_entry)) {
// this instruction is a noop - codegen can pass the pointer we already have as the result
ir_link_new_instruction(value, &ref_instruction->base);
return ptr_type;
} else {
fn_entry->alloca_list.append(&ref_instruction->base);
ir_build_ref_from(&ira->new_irb, &ref_instruction->base, value);
return ptr_type;
}
}
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) { switch (instruction->id) {
case IrInstructionIdInvalid: case IrInstructionIdInvalid:
@ -5778,6 +5832,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_import(ira, (IrInstructionImport *)instruction); return ir_analyze_instruction_import(ira, (IrInstructionImport *)instruction);
case IrInstructionIdArrayLen: case IrInstructionIdArrayLen:
return ir_analyze_instruction_array_len(ira, (IrInstructionArrayLen *)instruction); return ir_analyze_instruction_array_len(ira, (IrInstructionArrayLen *)instruction);
case IrInstructionIdRef:
return ir_analyze_instruction_ref(ira, (IrInstructionRef *)instruction);
case IrInstructionIdCast: case IrInstructionIdCast:
case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitList:
case IrInstructionIdContainerInitFields: case IrInstructionIdContainerInitFields:
@ -5901,6 +5957,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdSwitchTarget: case IrInstructionIdSwitchTarget:
case IrInstructionIdEnumTag: case IrInstructionIdEnumTag:
case IrInstructionIdStaticEval: case IrInstructionIdStaticEval:
case IrInstructionIdRef:
return false; return false;
case IrInstructionIdAsm: case IrInstructionIdAsm:
{ {

View File

@ -586,6 +586,11 @@ static void ir_print_array_len(IrPrint *irp, IrInstructionArrayLen *instruction)
fprintf(irp->f, ".len"); fprintf(irp->f, ".len");
} }
static void ir_print_ref(IrPrint *irp, IrInstructionRef *instruction) {
fprintf(irp->f, "ref ");
ir_print_other_instruction(irp, instruction->value);
}
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction); ir_print_prefix(irp, instruction);
switch (instruction->id) { switch (instruction->id) {
@ -714,6 +719,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdArrayLen: case IrInstructionIdArrayLen:
ir_print_array_len(irp, (IrInstructionArrayLen *)instruction); ir_print_array_len(irp, (IrInstructionArrayLen *)instruction);
break; break;
case IrInstructionIdRef:
ir_print_ref(irp, (IrInstructionRef *)instruction);
break;
} }
fprintf(irp->f, "\n"); fprintf(irp->f, "\n");
} }