mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 13:30:45 +00:00
IR: ability to assign to an array at runtime
This commit is contained in:
parent
114049a220
commit
8e2804efa1
@ -2857,18 +2857,45 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtr *instruction) {
|
||||
return LLVMBuildLoad(g->builder, ir_llvm_value(g, instruction->ptr), "");
|
||||
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
|
||||
return get_handle_value(g, ptr, instruction->base.type_entry);
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) {
|
||||
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
|
||||
LLVMValueRef value = ir_llvm_value(g, instruction->value);
|
||||
|
||||
assert(instruction->ptr->type_entry->id == TypeTableEntryIdPointer);
|
||||
TypeTableEntry *op1_type = instruction->ptr->type_entry->data.pointer.child_type;
|
||||
TypeTableEntry *op2_type = instruction->value->type_entry;
|
||||
|
||||
if (!type_has_bits(op1_type)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (handle_is_ptr(op1_type)) {
|
||||
assert(op1_type == op2_type);
|
||||
return gen_struct_memcpy(g, value, ptr, op1_type);
|
||||
}
|
||||
|
||||
LLVMBuildStore(g->builder, value, ptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrInstructionVarPtr *instruction) {
|
||||
return instruction->var->value_ref;
|
||||
VariableTableEntry *var = instruction->var;
|
||||
if (type_has_bits(var->type)) {
|
||||
assert(var->value_ref);
|
||||
return get_handle_value(g, var->value_ref, var->type);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrInstructionElemPtr *instruction) {
|
||||
LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array_ptr);
|
||||
LLVMValueRef subscript_value = ir_llvm_value(g, instruction->elem_index);
|
||||
TypeTableEntry *array_type = instruction->array_ptr->type_entry;
|
||||
return gen_array_elem_ptr(g, instruction->base.source_node, array_ptr, array_type, subscript_value);
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) {
|
||||
@ -2942,6 +2969,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
return ir_render_store_ptr(g, executable, (IrInstructionStorePtr *)instruction);
|
||||
case IrInstructionIdVarPtr:
|
||||
return ir_render_var_ptr(g, executable, (IrInstructionVarPtr *)instruction);
|
||||
case IrInstructionIdElemPtr:
|
||||
return ir_render_elem_ptr(g, executable, (IrInstructionElemPtr *)instruction);
|
||||
case IrInstructionIdCall:
|
||||
return ir_render_call(g, executable, (IrInstructionCall *)instruction);
|
||||
case IrInstructionIdSwitchBr:
|
||||
@ -2950,7 +2979,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
case IrInstructionIdContainerInitList:
|
||||
case IrInstructionIdContainerInitFields:
|
||||
case IrInstructionIdFieldPtr:
|
||||
case IrInstructionIdElemPtr:
|
||||
zig_panic("TODO render more IR instructions to LLVM");
|
||||
}
|
||||
zig_unreachable();
|
||||
|
||||
168
src/ir.cpp
168
src/ir.cpp
@ -30,7 +30,8 @@ struct IrAnalyze {
|
||||
};
|
||||
|
||||
static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, BlockContext *scope);
|
||||
static IrInstruction *ir_gen_lvalue(IrBuilder *irb, AstNode *node, BlockContext *scope, LValPurpose purpose);
|
||||
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context,
|
||||
LValPurpose lval);
|
||||
|
||||
static void ir_instruction_append(IrBasicBlock *basic_block, IrInstruction *instruction) {
|
||||
assert(basic_block);
|
||||
@ -334,6 +335,27 @@ static IrInstruction *ir_build_var_ptr_from(IrBuilder *irb, IrInstruction *old_i
|
||||
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, AstNode *source_node, IrInstruction *array_ptr,
|
||||
IrInstruction *elem_index)
|
||||
{
|
||||
IrInstructionElemPtr *instruction = ir_build_instruction<IrInstructionElemPtr>(irb, source_node);
|
||||
instruction->array_ptr = array_ptr;
|
||||
instruction->elem_index = elem_index;
|
||||
|
||||
ir_ref_instruction(array_ptr);
|
||||
ir_ref_instruction(elem_index);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||
IrInstruction *array_ptr, IrInstruction *elem_index)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_elem_ptr(irb, old_instruction->source_node, array_ptr, elem_index);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_call(IrBuilder *irb, AstNode *source_node,
|
||||
IrInstruction *fn, size_t arg_count, IrInstruction **args)
|
||||
{
|
||||
@ -677,7 +699,7 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, AstNode *node, IrBinOp op
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_assign(IrBuilder *irb, AstNode *node) {
|
||||
IrInstruction *lvalue = ir_gen_lvalue(irb, node->data.bin_op_expr.op1, node->block_context, LValPurposeAssign);
|
||||
IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, node->block_context, LValPurposeAssign);
|
||||
if (lvalue == irb->codegen->invalid_instruction)
|
||||
return lvalue;
|
||||
|
||||
@ -690,7 +712,7 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, AstNode *node) {
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_assign_op(IrBuilder *irb, AstNode *node, IrBinOp op_id) {
|
||||
IrInstruction *lvalue = ir_gen_lvalue(irb, node->data.bin_op_expr.op1, node->block_context, LValPurposeAssign);
|
||||
IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, node->block_context, LValPurposeAssign);
|
||||
if (lvalue == irb->codegen->invalid_instruction)
|
||||
return lvalue;
|
||||
IrInstruction *op1 = ir_build_load_ptr(irb, node->data.bin_op_expr.op1, lvalue);
|
||||
@ -882,6 +904,26 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, AstNode *node, LValPurpose l
|
||||
return irb->codegen->invalid_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_array_access(IrBuilder *irb, AstNode *node, LValPurpose lval) {
|
||||
assert(node->type == NodeTypeArrayAccessExpr);
|
||||
|
||||
AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr;
|
||||
IrInstruction *array_ref_instruction = ir_gen_node(irb, array_ref_node, node->block_context);
|
||||
if (array_ref_instruction == irb->codegen->invalid_instruction)
|
||||
return array_ref_instruction;
|
||||
|
||||
AstNode *subscript_node = node->data.array_access_expr.subscript;
|
||||
IrInstruction *subscript_instruction = ir_gen_node(irb, subscript_node, node->block_context);
|
||||
if (subscript_instruction == irb->codegen->invalid_instruction)
|
||||
return subscript_instruction;
|
||||
|
||||
IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, node, array_ref_instruction, subscript_instruction);
|
||||
if (lval != LValPurposeNone)
|
||||
return ptr_instruction;
|
||||
|
||||
return ir_build_load_ptr(irb, node, ptr_instruction);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
|
||||
assert(node->type == NodeTypeFnCallExpr);
|
||||
|
||||
@ -1151,7 +1193,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, AstNode *node) {
|
||||
return ir_build_const_void(irb, node);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context, LValPurpose lval) {
|
||||
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context,
|
||||
LValPurpose lval)
|
||||
{
|
||||
assert(block_context);
|
||||
node->block_context = block_context;
|
||||
|
||||
@ -1176,10 +1220,11 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont
|
||||
return ir_gen_var_decl(irb, node);
|
||||
case NodeTypeWhileExpr:
|
||||
return ir_gen_while_expr(irb, node);
|
||||
case NodeTypeArrayAccessExpr:
|
||||
return ir_gen_array_access(irb, node, lval);
|
||||
case NodeTypeUnwrapErrorExpr:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeDefer:
|
||||
case NodeTypeArrayAccessExpr:
|
||||
case NodeTypeSliceExpr:
|
||||
case NodeTypeFieldAccessExpr:
|
||||
case NodeTypeIfVarExpr:
|
||||
@ -1223,67 +1268,6 @@ static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, BlockContext *s
|
||||
return ir_gen_node_extra(irb, node, scope, LValPurposeNone);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_lvalue(IrBuilder *irb, AstNode *node, BlockContext *scope, LValPurpose lval) {
|
||||
assert(scope);
|
||||
node->block_context = scope;
|
||||
switch (node->type) {
|
||||
case NodeTypeSymbol:
|
||||
return ir_gen_symbol(irb, node, lval);
|
||||
case NodeTypeArrayAccessExpr:
|
||||
zig_panic("TODO array access lvalue");
|
||||
case NodeTypeFieldAccessExpr:
|
||||
zig_panic("TODO field access lvalue");
|
||||
case NodeTypePrefixOpExpr:
|
||||
zig_panic("TODO prefix op lvalue");
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeBinOpExpr:
|
||||
case NodeTypeNumberLiteral:
|
||||
case NodeTypeFnCallExpr:
|
||||
case NodeTypeIfBoolExpr:
|
||||
case NodeTypeContainerInitExpr:
|
||||
case NodeTypeVariableDeclaration:
|
||||
case NodeTypeWhileExpr:
|
||||
case NodeTypeUnwrapErrorExpr:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeDefer:
|
||||
case NodeTypeSliceExpr:
|
||||
case NodeTypeIfVarExpr:
|
||||
case NodeTypeForExpr:
|
||||
case NodeTypeAsmExpr:
|
||||
case NodeTypeGoto:
|
||||
case NodeTypeBreak:
|
||||
case NodeTypeContinue:
|
||||
case NodeTypeLabel:
|
||||
case NodeTypeSwitchExpr:
|
||||
case NodeTypeBoolLiteral:
|
||||
case NodeTypeStringLiteral:
|
||||
case NodeTypeCharLiteral:
|
||||
case NodeTypeNullLiteral:
|
||||
case NodeTypeUndefinedLiteral:
|
||||
case NodeTypeZeroesLiteral:
|
||||
case NodeTypeThisLiteral:
|
||||
case NodeTypeErrorType:
|
||||
case NodeTypeTypeLiteral:
|
||||
case NodeTypeArrayType:
|
||||
case NodeTypeVarLiteral:
|
||||
case NodeTypeRoot:
|
||||
case NodeTypeFnProto:
|
||||
case NodeTypeFnDef:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeUse:
|
||||
case NodeTypeContainerDecl:
|
||||
case NodeTypeStructField:
|
||||
case NodeTypeStructValueField:
|
||||
case NodeTypeSwitchProng:
|
||||
case NodeTypeSwitchRange:
|
||||
case NodeTypeErrorValueDecl:
|
||||
case NodeTypeTypeDecl:
|
||||
zig_unreachable();
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
IrInstruction *ir_gen(CodeGen *codegen, AstNode *node, BlockContext *scope, IrExecutable *ir_executable) {
|
||||
assert(node->owner);
|
||||
|
||||
@ -4034,6 +4018,46 @@ static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstruct
|
||||
return ptr_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) {
|
||||
IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other;
|
||||
IrInstruction *elem_index = elem_ptr_instruction->elem_index->other;
|
||||
|
||||
TypeTableEntry *array_type = array_ptr->type_entry;
|
||||
TypeTableEntry *return_type;
|
||||
|
||||
if (array_type->id == TypeTableEntryIdInvalid) {
|
||||
return array_type;
|
||||
} else if (array_type->id == TypeTableEntryIdArray) {
|
||||
if (array_type->data.array.len == 0) {
|
||||
add_node_error(ira->codegen, elem_ptr_instruction->base.source_node,
|
||||
buf_sprintf("out of bounds array access"));
|
||||
}
|
||||
TypeTableEntry *child_type = array_type->data.array.child_type;
|
||||
return_type = get_pointer_to_type(ira->codegen, child_type, false);
|
||||
} else if (array_type->id == TypeTableEntryIdPointer) {
|
||||
return_type = array_type;
|
||||
} else if (is_slice(array_type)) {
|
||||
return_type = array_type->data.structure.fields[0].type_entry;
|
||||
} else {
|
||||
add_node_error(ira->codegen, elem_ptr_instruction->base.source_node,
|
||||
buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name)));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
|
||||
IrInstruction *casted_elem_index = ir_get_casted_value(ira, elem_index, usize);
|
||||
if (casted_elem_index == ira->codegen->invalid_instruction)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
if (array_ptr->static_value.ok && casted_elem_index->static_value.ok) {
|
||||
zig_panic("TODO compile time array access");
|
||||
}
|
||||
|
||||
ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr, casted_elem_index);
|
||||
|
||||
return return_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) {
|
||||
IrInstruction *ptr = load_ptr_instruction->ptr->other;
|
||||
TypeTableEntry *type_entry = ptr->type_entry;
|
||||
@ -4125,7 +4149,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||
case IrInstructionIdFieldPtr:
|
||||
zig_panic("TODO field ptr");
|
||||
case IrInstructionIdElemPtr:
|
||||
zig_panic("TODO elem ptr");
|
||||
return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction);
|
||||
case IrInstructionIdVarPtr:
|
||||
return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction);
|
||||
case IrInstructionIdCall:
|
||||
@ -4149,16 +4173,12 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction,
|
||||
TypeTableEntry *expected_type)
|
||||
{
|
||||
static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) {
|
||||
TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction);
|
||||
instruction->type_entry = instruction_type;
|
||||
if (instruction->other)
|
||||
instruction->other->type_entry = instruction_type;
|
||||
|
||||
IrInstruction *casted_instruction = ir_get_casted_value(ira, instruction, expected_type);
|
||||
return casted_instruction->type_entry;
|
||||
return instruction_type;
|
||||
}
|
||||
|
||||
// This function attempts to evaluate IR code while doing type checking and other analysis.
|
||||
@ -4195,7 +4215,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
|
||||
continue;
|
||||
}
|
||||
|
||||
TypeTableEntry *return_type = ir_analyze_instruction(ira, old_instruction, nullptr);
|
||||
TypeTableEntry *return_type = ir_analyze_instruction(ira, old_instruction);
|
||||
|
||||
// unreachable instructions do their own control flow.
|
||||
if (return_type->id == TypeTableEntryIdUnreachable)
|
||||
|
||||
@ -320,6 +320,7 @@ static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruc
|
||||
}
|
||||
|
||||
static void ir_print_elem_ptr(IrPrint *irp, IrInstructionElemPtr *instruction) {
|
||||
fprintf(irp->f, "&");
|
||||
ir_print_other_instruction(irp, instruction->array_ptr);
|
||||
fprintf(irp->f, "[");
|
||||
ir_print_other_instruction(irp, instruction->elem_index);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user