Commit 8e2804efa1

Andrew Kelley <superjoe30@gmail.com>
2016-10-28 08:32:36
IR: ability to assign to an array at runtime
1 parent 114049a
Changed files (3)
src/codegen.cpp
@@ -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();
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)
src/ir_print.cpp
@@ -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);