Commit d7a2b05a81

Andrew Kelley <superjoe30@gmail.com>
2016-10-23 06:21:29
IR: introduce concept of lvalues
1 parent a9a6f77
src/all_types.hpp
@@ -34,7 +34,7 @@ struct IrBasicBlock;
 
 struct IrExecutable {
     ZigList<IrBasicBlock *> basic_block_list;
-    size_t var_slot_count;
+    size_t mem_slot_count;
     size_t next_debug_id;
 };
 
@@ -1349,7 +1349,7 @@ struct VariableTableEntry {
     bool force_depends_on_compile_var;
     ImportTableEntry *import;
     bool shadowable;
-    size_t slot_index;
+    size_t mem_slot_index;
     size_t ref_count;
 };
 
@@ -1425,8 +1425,11 @@ enum IrInstructionId {
     IrInstructionIdUnOp,
     IrInstructionIdBinOp,
     IrInstructionIdDeclVar,
-    IrInstructionIdLoadVar,
-    IrInstructionIdStoreVar,
+    IrInstructionIdLoadPtr,
+    IrInstructionIdStorePtr,
+    IrInstructionIdFieldPtr,
+    IrInstructionIdElemPtr,
+    IrInstructionIdVarPtr,
     IrInstructionIdCall,
     IrInstructionIdBuiltinCall,
     IrInstructionIdConst,
@@ -1554,16 +1557,36 @@ struct IrInstructionDeclVar {
     IrInstruction *init_value;
 };
 
-struct IrInstructionLoadVar {
+struct IrInstructionLoadPtr {
     IrInstruction base;
 
-    VariableTableEntry *var;
+    IrInstruction *ptr;
 };
 
-struct IrInstructionStoreVar {
+struct IrInstructionStorePtr {
     IrInstruction base;
 
+    IrInstruction *ptr;
     IrInstruction *value;
+};
+
+struct IrInstructionFieldPtr {
+    IrInstruction base;
+
+    IrInstruction *struct_ptr;
+    Buf field_name;
+};
+
+struct IrInstructionElemPtr {
+    IrInstruction base;
+
+    IrInstruction *array_ptr;
+    IrInstruction *elem_index;
+};
+
+struct IrInstructionVarPtr {
+    IrInstruction base;
+
     VariableTableEntry *var;
 };
 
src/analyze.cpp
@@ -3615,6 +3615,9 @@ static VariableTableEntry *add_local_var_shadowable(CodeGen *g, AstNode *source_
         // TODO replace _anon with @anon and make sure all tests still pass
         buf_init_from_str(&variable_entry->name, "_anon");
     }
+    if (context->fn_entry) {
+        context->fn_entry->variable_list.append(variable_entry);
+    }
 
     variable_entry->is_const = is_const;
     variable_entry->decl_node = source_node;
src/codegen.cpp
@@ -2326,17 +2326,6 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
     return nullptr;
 }
 
-static LLVMValueRef ir_render_load_var(CodeGen *g, IrExecutable *executable,
-        IrInstructionLoadVar *load_var_instruction)
-{
-    VariableTableEntry *var = load_var_instruction->var;
-    if (!type_has_bits(var->type))
-        return nullptr;
-
-    assert(var->value_ref);
-    return get_handle_value(g, var->value_ref, var->type);
-}
-
 static LLVMValueRef ir_render_bin_op_bool(CodeGen *g, IrExecutable *executable,
         IrInstructionBinOp *bin_op_instruction)
 {
@@ -2864,6 +2853,14 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
     return nullptr;
 }
 
+static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtr *instruction) {
+    return LLVMBuildLoad(g->builder, ir_llvm_value(g, instruction->ptr), "");
+}
+
+static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrInstructionVarPtr *instruction) {
+    return instruction->var->value_ref;
+}
+
 static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) {
     set_debug_source_node(g, instruction->source_node);
 
@@ -2875,8 +2872,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
         case IrInstructionIdDeclVar:
             return ir_render_decl_var(g, executable, (IrInstructionDeclVar *)instruction);
-        case IrInstructionIdLoadVar:
-            return ir_render_load_var(g, executable, (IrInstructionLoadVar *)instruction);
         case IrInstructionIdBinOp:
             return ir_render_bin_op(g, executable, (IrInstructionBinOp *)instruction);
         case IrInstructionIdCast:
@@ -2889,13 +2884,19 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_br(g, executable, (IrInstructionBr *)instruction);
         case IrInstructionIdUnOp:
             return ir_render_un_op(g, executable, (IrInstructionUnOp *)instruction);
+        case IrInstructionIdLoadPtr:
+            return ir_render_load_ptr(g, executable, (IrInstructionLoadPtr *)instruction);
+        case IrInstructionIdVarPtr:
+            return ir_render_var_ptr(g, executable, (IrInstructionVarPtr *)instruction);
         case IrInstructionIdSwitchBr:
         case IrInstructionIdPhi:
-        case IrInstructionIdStoreVar:
+        case IrInstructionIdStorePtr:
         case IrInstructionIdCall:
         case IrInstructionIdBuiltinCall:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdContainerInitFields:
+        case IrInstructionIdFieldPtr:
+        case IrInstructionIdElemPtr:
             zig_panic("TODO render more IR instructions to LLVM");
     }
     zig_unreachable();
@@ -3152,86 +3153,51 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
     assert(node->data.while_expr.condition);
     assert(node->data.while_expr.body);
 
-    AstNode *continue_expr_node = node->data.while_expr.continue_expr;
+    //AstNode *continue_expr_node = node->data.while_expr.continue_expr;
 
     bool condition_always_true = node->data.while_expr.condition_always_true;
-    bool contains_break = node->data.while_expr.contains_break;
+    //bool contains_break = node->data.while_expr.contains_break;
     if (condition_always_true) {
         // generate a forever loop
-
-        LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
-        LLVMBasicBlockRef continue_block = continue_expr_node ?
-            LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileContinue") : body_block;
-        LLVMBasicBlockRef end_block = nullptr;
-        if (contains_break) {
-            end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
-        }
-
-        set_debug_source_node(g, node);
-        LLVMBuildBr(g->builder, body_block);
-
-        if (continue_expr_node) {
-            LLVMPositionBuilderAtEnd(g->builder, continue_block);
-
-            gen_expr(g, continue_expr_node);
-
-            set_debug_source_node(g, node);
-            LLVMBuildBr(g->builder, body_block);
-        }
-
-        LLVMPositionBuilderAtEnd(g->builder, body_block);
-        g->break_block_stack.append(end_block);
-        g->continue_block_stack.append(continue_block);
-        gen_expr(g, node->data.while_expr.body);
-        g->break_block_stack.pop();
-        g->continue_block_stack.pop();
-
-        if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
-            set_debug_source_node(g, node);
-            LLVMBuildBr(g->builder, continue_block);
-        }
-
-        if (contains_break) {
-            LLVMPositionBuilderAtEnd(g->builder, end_block);
-        }
+        zig_panic("TODO IR");
+
+        //LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
+        //LLVMBasicBlockRef continue_block = continue_expr_node ?
+        //    LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileContinue") : body_block;
+        //LLVMBasicBlockRef end_block = nullptr;
+        //if (contains_break) {
+        //    end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
+        //}
+
+        //set_debug_source_node(g, node);
+        //LLVMBuildBr(g->builder, body_block);
+
+        //if (continue_expr_node) {
+        //    LLVMPositionBuilderAtEnd(g->builder, continue_block);
+
+        //    gen_expr(g, continue_expr_node);
+
+        //    set_debug_source_node(g, node);
+        //    LLVMBuildBr(g->builder, body_block);
+        //}
+
+        //LLVMPositionBuilderAtEnd(g->builder, body_block);
+        //g->break_block_stack.append(end_block);
+        //g->continue_block_stack.append(continue_block);
+        //gen_expr(g, node->data.while_expr.body);
+        //g->break_block_stack.pop();
+        //g->continue_block_stack.pop();
+
+        //if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
+        //    set_debug_source_node(g, node);
+        //    LLVMBuildBr(g->builder, continue_block);
+        //}
+
+        //if (contains_break) {
+        //    LLVMPositionBuilderAtEnd(g->builder, end_block);
+        //}
     } else {
-        // generate a normal while loop
-
-        LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileCond");
-        LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
-        LLVMBasicBlockRef continue_block = continue_expr_node ?
-            LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileContinue") : cond_block;
-        LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
-
-        set_debug_source_node(g, node);
-        LLVMBuildBr(g->builder, cond_block);
-
-        if (continue_expr_node) {
-            LLVMPositionBuilderAtEnd(g->builder, continue_block);
-
-            gen_expr(g, continue_expr_node);
-
-            set_debug_source_node(g, node);
-            LLVMBuildBr(g->builder, cond_block);
-        }
-
-        LLVMPositionBuilderAtEnd(g->builder, cond_block);
-        LLVMValueRef cond_val = gen_expr(g, node->data.while_expr.condition);
-        set_debug_source_node(g, node->data.while_expr.condition);
-        LLVMBuildCondBr(g->builder, cond_val, body_block, end_block);
-
-        LLVMPositionBuilderAtEnd(g->builder, body_block);
-        g->break_block_stack.append(end_block);
-        g->continue_block_stack.append(continue_block);
-        gen_expr(g, node->data.while_expr.body);
-        g->break_block_stack.pop();
-        g->continue_block_stack.pop();
-        if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
-            set_debug_source_node(g, node);
-            LLVMBuildBr(g->builder, continue_block);
-        }
-
-        LLVMPositionBuilderAtEnd(g->builder, end_block);
+        zig_panic("moved to ir.cpp");
     }
 
     return nullptr;
@@ -3242,98 +3208,99 @@ static LLVMValueRef gen_for_expr(CodeGen *g, AstNode *node) {
     assert(node->data.for_expr.array_expr);
     assert(node->data.for_expr.body);
 
-    VariableTableEntry *elem_var = node->data.for_expr.elem_var;
-    assert(elem_var);
-
-    TypeTableEntry *array_type = get_expr_type(node->data.for_expr.array_expr);
-
-    VariableTableEntry *index_var = node->data.for_expr.index_var;
-    assert(index_var);
-    LLVMValueRef index_ptr = index_var->value_ref;
-    LLVMValueRef one_const = LLVMConstInt(g->builtin_types.entry_usize->type_ref, 1, false);
-
-    LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForCond");
-    LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForBody");
-    LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForEnd");
-    LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForContinue");
-
-    LLVMValueRef array_val = gen_array_base_ptr(g, node->data.for_expr.array_expr);
-    set_debug_source_node(g, node);
-    LLVMBuildStore(g->builder, LLVMConstNull(index_var->type->type_ref), index_ptr);
-
-    gen_var_debug_decl(g, index_var);
-
-    LLVMValueRef len_val;
-    TypeTableEntry *child_type;
-    if (array_type->id == TypeTableEntryIdArray) {
-        len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
-                array_type->data.array.len, false);
-        child_type = array_type->data.array.child_type;
-    } else if (array_type->id == TypeTableEntryIdStruct) {
-        assert(array_type->data.structure.is_slice);
-        TypeTableEntry *child_ptr_type = array_type->data.structure.fields[0].type_entry;
-        assert(child_ptr_type->id == TypeTableEntryIdPointer);
-        child_type = child_ptr_type->data.pointer.child_type;
-        size_t len_index = array_type->data.structure.fields[1].gen_index;
-        assert(len_index != SIZE_MAX);
-        LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, array_val, len_index, "");
-        len_val = LLVMBuildLoad(g->builder, len_field_ptr, "");
-    } else {
-        zig_unreachable();
-    }
-    LLVMBuildBr(g->builder, cond_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, cond_block);
-    LLVMValueRef index_val = LLVMBuildLoad(g->builder, index_ptr, "");
-    LLVMValueRef cond = LLVMBuildICmp(g->builder, LLVMIntSLT, index_val, len_val, "");
-    LLVMBuildCondBr(g->builder, cond, body_block, end_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, body_block);
-    LLVMValueRef elem_ptr = gen_array_elem_ptr(g, node, array_val, array_type, index_val);
-
-    LLVMValueRef elem_val;
-    if (node->data.for_expr.elem_is_ptr) {
-        elem_val = elem_ptr;
-    } else {
-        elem_val = handle_is_ptr(child_type) ? elem_ptr : LLVMBuildLoad(g->builder, elem_ptr, "");
-    }
-    gen_assign_raw(g, node, BinOpTypeAssign, elem_var->value_ref, elem_val, elem_var->type, child_type);
-    gen_var_debug_decl(g, elem_var);
-    g->break_block_stack.append(end_block);
-    g->continue_block_stack.append(continue_block);
-    gen_expr(g, node->data.for_expr.body);
-    g->break_block_stack.pop();
-    g->continue_block_stack.pop();
-    if (get_expr_type(node->data.for_expr.body)->id != TypeTableEntryIdUnreachable) {
-        set_debug_source_node(g, node);
-        LLVMBuildBr(g->builder, continue_block);
-    }
-
-    LLVMPositionBuilderAtEnd(g->builder, continue_block);
-    set_debug_source_node(g, node);
-    LLVMValueRef new_index_val = LLVMBuildNSWAdd(g->builder, index_val, one_const, "");
-    LLVMBuildStore(g->builder, new_index_val, index_ptr);
-    LLVMBuildBr(g->builder, cond_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, end_block);
-    return nullptr;
-}
-
-static LLVMValueRef gen_break(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeBreak);
-    LLVMBasicBlockRef dest_block = g->break_block_stack.last();
-
-    set_debug_source_node(g, node);
-    return LLVMBuildBr(g->builder, dest_block);
-}
-
-static LLVMValueRef gen_continue(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeContinue);
-    LLVMBasicBlockRef dest_block = g->continue_block_stack.last();
-
-    set_debug_source_node(g, node);
-    return LLVMBuildBr(g->builder, dest_block);
-}
+    zig_panic("TODO IR for loop");
+    //VariableTableEntry *elem_var = node->data.for_expr.elem_var;
+    //assert(elem_var);
+
+    //TypeTableEntry *array_type = get_expr_type(node->data.for_expr.array_expr);
+
+    //VariableTableEntry *index_var = node->data.for_expr.index_var;
+    //assert(index_var);
+    //LLVMValueRef index_ptr = index_var->value_ref;
+    //LLVMValueRef one_const = LLVMConstInt(g->builtin_types.entry_usize->type_ref, 1, false);
+
+    //LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForCond");
+    //LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForBody");
+    //LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForEnd");
+    //LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForContinue");
+
+    //LLVMValueRef array_val = gen_array_base_ptr(g, node->data.for_expr.array_expr);
+    //set_debug_source_node(g, node);
+    //LLVMBuildStore(g->builder, LLVMConstNull(index_var->type->type_ref), index_ptr);
+
+    //gen_var_debug_decl(g, index_var);
+
+    //LLVMValueRef len_val;
+    //TypeTableEntry *child_type;
+    //if (array_type->id == TypeTableEntryIdArray) {
+    //    len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
+    //            array_type->data.array.len, false);
+    //    child_type = array_type->data.array.child_type;
+    //} else if (array_type->id == TypeTableEntryIdStruct) {
+    //    assert(array_type->data.structure.is_slice);
+    //    TypeTableEntry *child_ptr_type = array_type->data.structure.fields[0].type_entry;
+    //    assert(child_ptr_type->id == TypeTableEntryIdPointer);
+    //    child_type = child_ptr_type->data.pointer.child_type;
+    //    size_t len_index = array_type->data.structure.fields[1].gen_index;
+    //    assert(len_index != SIZE_MAX);
+    //    LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, array_val, len_index, "");
+    //    len_val = LLVMBuildLoad(g->builder, len_field_ptr, "");
+    //} else {
+    //    zig_unreachable();
+    //}
+    //LLVMBuildBr(g->builder, cond_block);
+
+    //LLVMPositionBuilderAtEnd(g->builder, cond_block);
+    //LLVMValueRef index_val = LLVMBuildLoad(g->builder, index_ptr, "");
+    //LLVMValueRef cond = LLVMBuildICmp(g->builder, LLVMIntSLT, index_val, len_val, "");
+    //LLVMBuildCondBr(g->builder, cond, body_block, end_block);
+
+    //LLVMPositionBuilderAtEnd(g->builder, body_block);
+    //LLVMValueRef elem_ptr = gen_array_elem_ptr(g, node, array_val, array_type, index_val);
+
+    //LLVMValueRef elem_val;
+    //if (node->data.for_expr.elem_is_ptr) {
+    //    elem_val = elem_ptr;
+    //} else {
+    //    elem_val = handle_is_ptr(child_type) ? elem_ptr : LLVMBuildLoad(g->builder, elem_ptr, "");
+    //}
+    //gen_assign_raw(g, node, BinOpTypeAssign, elem_var->value_ref, elem_val, elem_var->type, child_type);
+    //gen_var_debug_decl(g, elem_var);
+    //g->break_block_stack.append(end_block);
+    //g->continue_block_stack.append(continue_block);
+    //gen_expr(g, node->data.for_expr.body);
+    //g->break_block_stack.pop();
+    //g->continue_block_stack.pop();
+    //if (get_expr_type(node->data.for_expr.body)->id != TypeTableEntryIdUnreachable) {
+    //    set_debug_source_node(g, node);
+    //    LLVMBuildBr(g->builder, continue_block);
+    //}
+
+    //LLVMPositionBuilderAtEnd(g->builder, continue_block);
+    //set_debug_source_node(g, node);
+    //LLVMValueRef new_index_val = LLVMBuildNSWAdd(g->builder, index_val, one_const, "");
+    //LLVMBuildStore(g->builder, new_index_val, index_ptr);
+    //LLVMBuildBr(g->builder, cond_block);
+
+    //LLVMPositionBuilderAtEnd(g->builder, end_block);
+    //return nullptr;
+}
+
+//static LLVMValueRef gen_break(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeBreak);
+//    LLVMBasicBlockRef dest_block = g->break_block_stack.last();
+//
+//    set_debug_source_node(g, node);
+//    return LLVMBuildBr(g->builder, dest_block);
+//}
+
+//static LLVMValueRef gen_continue(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeContinue);
+//    LLVMBasicBlockRef dest_block = g->continue_block_stack.last();
+//
+//    set_debug_source_node(g, node);
+//    return LLVMBuildBr(g->builder, dest_block);
+//}
 
 static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl,
         bool unwrap_maybe, LLVMValueRef *init_value, TypeTableEntry **expr_type, bool var_is_ptr)
@@ -3735,9 +3702,9 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
         case NodeTypeGoto:
             return gen_goto(g, node);
         case NodeTypeBreak:
-            return gen_break(g, node);
+            zig_panic("TODO IR");
         case NodeTypeContinue:
-            return gen_continue(g, node);
+            zig_panic("TODO IR");
         case NodeTypeLabel:
             return gen_label(g, node);
         case NodeTypeContainerInitExpr:
src/ir.cpp
@@ -3,20 +3,17 @@
 #include "eval.hpp"
 #include "ir.hpp"
 
-struct IrVarSlot {
-    ConstExprValue value;
-    bool runtime;
-};
-
 struct IrExecContext {
-    IrVarSlot *var_slot_list;
-    size_t var_slot_count;
+    ConstExprValue *mem_slot_list;
+    size_t mem_slot_count;
 };
 
 struct IrBuilder {
     CodeGen *codegen;
     IrExecutable *exec;
     IrBasicBlock *current_basic_block;
+    ZigList<IrBasicBlock *> break_block_stack;
+    ZigList<IrBasicBlock *> continue_block_stack;
 };
 
 struct IrAnalyze {
@@ -27,6 +24,7 @@ struct IrAnalyze {
 };
 
 static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, BlockContext *scope);
+static IrInstruction *ir_gen_lvalue(IrBuilder *irb, AstNode *node, BlockContext *scope);
 
 static void ir_instruction_append(IrBasicBlock *basic_block, IrInstruction *instruction) {
     assert(basic_block);
@@ -40,9 +38,9 @@ static size_t exec_next_debug_id(IrExecutable *exec) {
     return result;
 }
 
-static size_t exec_next_var_slot(IrExecutable *exec) {
-    size_t result = exec->var_slot_count;
-    exec->var_slot_count += 1;
+static size_t exec_next_mem_slot(IrExecutable *exec) {
+    size_t result = exec->mem_slot_count;
+    exec->mem_slot_count += 1;
     return result;
 }
 
@@ -115,12 +113,24 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVar *) {
     return IrInstructionIdDeclVar;
 }
 
-static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadVar *) {
-    return IrInstructionIdLoadVar;
+static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtr *) {
+    return IrInstructionIdLoadPtr;
 }
 
-static constexpr IrInstructionId ir_instruction_id(IrInstructionStoreVar *) {
-    return IrInstructionIdStoreVar;
+static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) {
+    return IrInstructionIdStorePtr;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldPtr *) {
+    return IrInstructionIdFieldPtr;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) {
+    return IrInstructionIdElemPtr;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionVarPtr *) {
+    return IrInstructionIdVarPtr;
 }
 
 static constexpr IrInstructionId ir_instruction_id(IrInstructionCall *) {
@@ -283,6 +293,37 @@ static IrInstruction *ir_build_const_generic_fn(IrBuilder *irb, AstNode *source_
     return &const_instruction->base;
 }
 
+static IrInstruction *ir_build_const_ptr(IrBuilder *irb, AstNode *source_node, ConstExprValue *pointee) {
+    IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node);
+    const_instruction->base.static_value.ok = true;
+    const_instruction->base.static_value.data.x_ptr.len = 1;
+    const_instruction->base.static_value.data.x_ptr.is_c_str = false;
+    const_instruction->base.static_value.data.x_ptr.ptr = allocate<ConstExprValue *>(1);
+    const_instruction->base.static_value.data.x_ptr.ptr[0] = pointee;
+    return &const_instruction->base;
+}
+
+static IrInstruction *ir_build_const_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
+        ConstExprValue *pointee)
+{
+    IrInstruction *new_instruction = ir_build_const_ptr(irb, old_instruction->source_node, pointee);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
+static IrInstruction *ir_build_const(IrBuilder *irb, AstNode *source_node, ConstExprValue *value) {
+    IrInstructionConst *instruction = ir_build_instruction<IrInstructionConst>(irb, source_node);
+    instruction->base.static_value = *value;
+    instruction->base.static_value.ok = true;
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_const_from(IrBuilder *irb, IrInstruction *old_instruction, ConstExprValue *value) {
+    IrInstruction *new_instruction = ir_build_const(irb, old_instruction->source_node, value);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
 static IrInstruction *ir_build_bin_op(IrBuilder *irb, AstNode *source_node, IrBinOp op_id,
         IrInstruction *op1, IrInstruction *op2)
 {
@@ -305,20 +346,19 @@ static IrInstruction *ir_build_bin_op_from(IrBuilder *irb, IrInstruction *old_in
     return new_instruction;
 }
 
-static IrInstruction *ir_build_load_var(IrBuilder *irb, AstNode *source_node, VariableTableEntry *var) {
-    IrInstructionLoadVar *load_var_instruction = ir_build_instruction<IrInstructionLoadVar>(irb, source_node);
-    load_var_instruction->base.type_entry = var->type;
-    load_var_instruction->var = var;
+static IrInstruction *ir_build_var_ptr(IrBuilder *irb, AstNode *source_node, VariableTableEntry *var) {
+    IrInstructionVarPtr *instruction = ir_build_instruction<IrInstructionVarPtr>(irb, source_node);
+    instruction->var = var;
 
     ir_ref_var(var);
 
-    return &load_var_instruction->base;
+    return &instruction->base;
 }
 
-static IrInstruction *ir_build_load_var_from(IrBuilder *irb, IrInstruction *old_instruction,
+static IrInstruction *ir_build_var_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
         VariableTableEntry *var)
 {
-    IrInstruction *new_instruction = ir_build_load_var(irb, old_instruction->source_node, var);
+    IrInstruction *new_instruction = ir_build_var_ptr(irb, old_instruction->source_node, var);
     ir_link_new_instruction(new_instruction, old_instruction);
     return new_instruction;
 
@@ -460,16 +500,20 @@ static IrInstruction *ir_build_unreachable_from(IrBuilder *irb, IrInstruction *o
     return new_instruction;
 }
 
-//static IrInstruction *ir_build_store(IrBuilder *irb, AstNode *source_node,
-//        VariableTableEntry *var, IrInstruction *value)
-//{
-//    IrInstructionStoreVar *store_instruction = ir_build_instruction<IrInstructionStoreVar>(irb, source_node);
-//    store_instruction->base.static_value.ok = true;
-//    store_instruction->base.type_entry = irb->codegen->builtin_types.entry_void;
-//    store_instruction->var = var;
-//    store_instruction->value = value;
-//    return &store_instruction->base;
-//}
+static IrInstruction *ir_build_store_ptr(IrBuilder *irb, AstNode *source_node,
+        IrInstruction *ptr, IrInstruction *value)
+{
+    IrInstructionStorePtr *instruction = ir_build_instruction<IrInstructionStorePtr>(irb, source_node);
+    instruction->base.static_value.ok = true;
+    instruction->base.type_entry = irb->codegen->builtin_types.entry_void;
+    instruction->ptr = ptr;
+    instruction->value = value;
+
+    ir_ref_instruction(ptr);
+    ir_ref_instruction(value);
+
+    return &instruction->base;
+}
 
 static IrInstruction *ir_build_var_decl(IrBuilder *irb, AstNode *source_node,
         VariableTableEntry *var, IrInstruction *var_type, IrInstruction *init_value)
@@ -480,6 +524,10 @@ static IrInstruction *ir_build_var_decl(IrBuilder *irb, AstNode *source_node,
     decl_var_instruction->var = var;
     decl_var_instruction->var_type = var_type;
     decl_var_instruction->init_value = init_value;
+
+    ir_ref_instruction(var_type);
+    ir_ref_instruction(init_value);
+
     return &decl_var_instruction->base;
 }
 
@@ -491,6 +539,21 @@ static IrInstruction *ir_build_var_decl_from(IrBuilder *irb, IrInstruction *old_
     return new_instruction;
 }
 
+static IrInstruction *ir_build_load_ptr(IrBuilder *irb, AstNode *source_node, IrInstruction *ptr) {
+    IrInstructionLoadPtr *instruction = ir_build_instruction<IrInstructionLoadPtr>(irb, source_node);
+    instruction->ptr = ptr;
+
+    ir_ref_instruction(ptr);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_load_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr) {
+    IrInstruction *new_instruction = ir_build_load_ptr(irb, old_instruction->source_node, ptr);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
 //static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
 //    size_t result = 0;
 //    while (inner_block != outer_block) {
@@ -552,7 +615,7 @@ static VariableTableEntry *ir_add_local_var(IrBuilder *irb, AstNode *node, Buf *
     variable_entry->block_context = node->block_context;
     variable_entry->import = node->owner;
     variable_entry->shadowable = is_shadowable;
-    variable_entry->slot_index = exec_next_var_slot(irb->exec);
+    variable_entry->mem_slot_index = exec_next_mem_slot(irb->exec);
 
     if (name) {
         buf_init_from_buf(&variable_entry->name, name);
@@ -625,6 +688,19 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, AstNode *node, IrBinOp op
     return ir_build_bin_op(irb, node, op_id, op1, op2);
 }
 
+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);
+    if (lvalue == irb->codegen->invalid_instruction)
+        return lvalue;
+    IrInstruction *op1 = ir_build_load_ptr(irb, node->data.bin_op_expr.op1, lvalue);
+    IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, node->block_context);
+    if (op2 == irb->codegen->invalid_instruction)
+        return op2;
+    IrInstruction *result = ir_build_bin_op(irb, node, op_id, op1, op2);
+    ir_build_store_ptr(irb, node, lvalue, result);
+    return ir_build_const_void(irb, node);
+}
+
 static IrInstruction *ir_gen_bin_op(IrBuilder *irb, AstNode *node) {
     assert(node->type == NodeTypeBinOpExpr);
 
@@ -633,23 +709,39 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, AstNode *node) {
         case BinOpTypeInvalid:
             zig_unreachable();
         case BinOpTypeAssign:
+            zig_panic("TODO gen IR for assignment");
         case BinOpTypeAssignTimes:
+            return ir_gen_assign_op(irb, node, IrBinOpMult);
         case BinOpTypeAssignTimesWrap:
+            return ir_gen_assign_op(irb, node, IrBinOpMultWrap);
         case BinOpTypeAssignDiv:
+            return ir_gen_assign_op(irb, node, IrBinOpDiv);
         case BinOpTypeAssignMod:
+            return ir_gen_assign_op(irb, node, IrBinOpMod);
         case BinOpTypeAssignPlus:
+            return ir_gen_assign_op(irb, node, IrBinOpAdd);
         case BinOpTypeAssignPlusWrap:
+            return ir_gen_assign_op(irb, node, IrBinOpAddWrap);
         case BinOpTypeAssignMinus:
+            return ir_gen_assign_op(irb, node, IrBinOpSub);
         case BinOpTypeAssignMinusWrap:
+            return ir_gen_assign_op(irb, node, IrBinOpSubWrap);
         case BinOpTypeAssignBitShiftLeft:
+            return ir_gen_assign_op(irb, node, IrBinOpBitShiftLeft);
         case BinOpTypeAssignBitShiftLeftWrap:
+            return ir_gen_assign_op(irb, node, IrBinOpBitShiftLeftWrap);
         case BinOpTypeAssignBitShiftRight:
+            return ir_gen_assign_op(irb, node, IrBinOpBitShiftRight);
         case BinOpTypeAssignBitAnd:
+            return ir_gen_assign_op(irb, node, IrBinOpBinAnd);
         case BinOpTypeAssignBitXor:
+            return ir_gen_assign_op(irb, node, IrBinOpBinXor);
         case BinOpTypeAssignBitOr:
+            return ir_gen_assign_op(irb, node, IrBinOpBinOr);
         case BinOpTypeAssignBoolAnd:
+            return ir_gen_assign_op(irb, node, IrBinOpBoolAnd);
         case BinOpTypeAssignBoolOr:
-            zig_panic("TODO gen IR for assignment");
+            return ir_gen_assign_op(irb, node, IrBinOpBoolOr);
         case BinOpTypeBoolOr:
         case BinOpTypeBoolAnd:
             // note: this is not a direct mapping to IrBinOpBoolOr/And
@@ -726,7 +818,8 @@ static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, AstN
 
     if (decl_node->type == NodeTypeVariableDeclaration) {
         VariableTableEntry *var = decl_node->data.variable_declaration.variable;
-        return ir_build_load_var(irb, source_node, var);
+        IrInstruction *var_ptr = ir_build_var_ptr(irb, source_node, var);
+        return ir_build_load_ptr(irb, source_node, var_ptr);
     } else if (decl_node->type == NodeTypeFnProto) {
         FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry;
         assert(fn_entry->type_entry);
@@ -763,8 +856,10 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, AstNode *node, bool pointer_
         return ir_build_const_type(irb, node, primitive_table_entry->value);
 
     VariableTableEntry *var = find_variable(irb->codegen, node->block_context, variable_name);
-    if (var)
-        return ir_build_load_var(irb, node, var);
+    if (var) {
+        IrInstruction *var_ptr = ir_build_var_ptr(irb, node, var);
+        return ir_build_load_ptr(irb, node, var_ptr);
+    }
 
     AstNode *decl_node = find_decl(node->block_context, variable_name);
     if (decl_node)
@@ -1011,6 +1106,44 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, AstNode *node) {
     return ir_build_var_decl(irb, node, var, type_instruction, init_value);
 }
 
+static IrInstruction *ir_gen_while_expr(IrBuilder *irb, AstNode *node) {
+    assert(node->type == NodeTypeWhileExpr);
+
+    AstNode *continue_expr_node = node->data.while_expr.continue_expr;
+
+    IrBasicBlock *cond_block = ir_build_basic_block(irb, "WhileCond");
+    IrBasicBlock *body_block = ir_build_basic_block(irb, "WhileBody");
+    IrBasicBlock *continue_block = continue_expr_node ?
+        ir_build_basic_block(irb, "WhileContinue") : cond_block;
+    IrBasicBlock *end_block = ir_build_basic_block(irb, "WhileEnd");
+
+    ir_build_br(irb, node, cond_block);
+
+    if (continue_expr_node) {
+        ir_set_cursor_at_end(irb, continue_block);
+        ir_gen_node(irb, continue_expr_node, node->block_context);
+        ir_build_br(irb, node, cond_block);
+
+    }
+
+    ir_set_cursor_at_end(irb, cond_block);
+    IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, node->block_context);
+    ir_build_cond_br(irb, node->data.while_expr.condition, cond_val, body_block, end_block);
+
+    ir_set_cursor_at_end(irb, body_block);
+
+    irb->break_block_stack.append(end_block);
+    irb->continue_block_stack.append(continue_block);
+    ir_gen_node(irb, node->data.while_expr.body, node->block_context);
+    irb->break_block_stack.pop();
+    irb->continue_block_stack.pop();
+
+    ir_build_br(irb, node, continue_block);
+    ir_set_cursor_at_end(irb, end_block);
+
+    return ir_build_const_void(irb, node);
+}
+
 static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context,
         bool pointer_only)
 {
@@ -1036,6 +1169,8 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont
             return ir_gen_container_init_expr(irb, node);
         case NodeTypeVariableDeclaration:
             return ir_gen_var_decl(irb, node);
+        case NodeTypeWhileExpr:
+            return ir_gen_while_expr(irb, node);
         case NodeTypeUnwrapErrorExpr:
         case NodeTypeReturnExpr:
         case NodeTypeDefer:
@@ -1043,7 +1178,6 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont
         case NodeTypeSliceExpr:
         case NodeTypeFieldAccessExpr:
         case NodeTypeIfVarExpr:
-        case NodeTypeWhileExpr:
         case NodeTypeForExpr:
         case NodeTypeAsmExpr:
         case NodeTypeGoto:
@@ -1085,6 +1219,67 @@ static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, BlockContext *s
     return ir_gen_node_extra(irb, node, scope, pointer_only_no);
 }
 
+static IrInstruction *ir_gen_lvalue(IrBuilder *irb, AstNode *node, BlockContext *scope) {
+    assert(scope);
+    node->block_context = scope;
+    switch (node->type) {
+        case NodeTypeSymbol:
+            zig_panic("TODO symbol lvalue");
+        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();
+}
+
 static IrInstruction *ir_gen_add_return(CodeGen *g, AstNode *node, BlockContext *scope,
         IrExecutable *ir_executable, bool add_return, bool pointer_only)
 {
@@ -1965,8 +2160,8 @@ static int ir_eval_math_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
 }
 
 static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
-    IrInstruction *op1 = bin_op_instruction->op1;
-    IrInstruction *op2 = bin_op_instruction->op2;
+    IrInstruction *op1 = bin_op_instruction->op1->other;
+    IrInstruction *op2 = bin_op_instruction->op2->other;
     IrInstruction *instructions[] = {op1, op2};
     TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, &bin_op_instruction->base, instructions, 2);
     if (resolved_type->id == TypeTableEntryIdInvalid)
@@ -2021,7 +2216,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
 
     }
 
-    ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, op1->other, op2->other);
+    ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, op1, op2);
 
     return resolved_type;
 }
@@ -2169,9 +2364,9 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
     var->type = result_type;
     assert(var->type != nullptr); // should have been caught by the parser
 
-    if (casted_init_value->static_value.ok) {
-        // TODO set the variable in the IrVarSlot
-    }
+    ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
+    *mem_slot = casted_init_value->static_value;
+
     ir_build_var_decl_from(&ira->new_irb, &decl_var_instruction->base, var, var_type, casted_init_value);
 
     BlockContext *scope = decl_var_instruction->base.source_node->block_context;
@@ -2181,11 +2376,6 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
     return ira->codegen->builtin_types.entry_void;
 }
 
-static TypeTableEntry *ir_analyze_instruction_load_var(IrAnalyze *ira, IrInstructionLoadVar *load_var_instruction) {
-    ir_build_load_var_from(&ira->new_irb, &load_var_instruction->base, load_var_instruction->var);
-    return load_var_instruction->var->type;
-}
-
 static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) {
     IrInstruction *fn_ref = call_instruction->fn->other;
     if (fn_ref->type_entry->id == TypeTableEntryIdInvalid)
@@ -2345,20 +2535,7 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
             //    }
             //}
         case IrUnOpDereference:
-            zig_panic("TODO analyze PrefixOpDereference");
-            //{
-            //    TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
-            //    if (type_entry->id == TypeTableEntryIdInvalid) {
-            //        return type_entry;
-            //    } else if (type_entry->id == TypeTableEntryIdPointer) {
-            //        return type_entry->data.pointer.child_type;
-            //    } else {
-            //        add_node_error(g, *expr_node,
-            //            buf_sprintf("indirection requires pointer operand ('%s' invalid)",
-            //                buf_ptr(&type_entry->name)));
-            //        return g->builtin_types.entry_invalid;
-            //    }
-            //}
+            zig_panic("TODO remove this IrUnOp item");
         case IrUnOpMaybe:
             zig_panic("TODO analyze PrefixOpMaybe");
             //{
@@ -3727,6 +3904,43 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
     return resolved_type;
 }
 
+static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) {
+    VariableTableEntry *var = var_ptr_instruction->var;
+    ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
+    TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, var_ptr_instruction->var->type, false);
+    if (mem_slot->ok) {
+        ir_build_const_ptr_from(&ira->new_irb, &var_ptr_instruction->base, mem_slot);
+        return ptr_type;
+    }
+
+    ir_build_var_ptr_from(&ira->new_irb, &var_ptr_instruction->base, var);
+    return ptr_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;
+    if (type_entry->id == TypeTableEntryIdInvalid) {
+        return type_entry;
+    } else if (type_entry->id == TypeTableEntryIdPointer) {
+        TypeTableEntry *child_type = type_entry->data.pointer.child_type;
+        if (ptr->static_value.ok) {
+            ConstExprValue *pointee = ptr->static_value.data.x_ptr.ptr[0];
+            if (pointee->ok) {
+                ir_build_const_from(&ira->new_irb, &load_ptr_instruction->base, pointee);
+                return child_type;
+            }
+        }
+        ir_build_load_ptr_from(&ira->new_irb, &load_ptr_instruction->base, ptr);
+        return child_type;
+    } else {
+        add_node_error(ira->codegen, load_ptr_instruction->base.source_node,
+            buf_sprintf("indirection requires pointer operand ('%s' invalid)",
+                buf_ptr(&type_entry->name)));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+}
+
 static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -3741,8 +3955,16 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction);
         case IrInstructionIdDeclVar:
             return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction);
-        case IrInstructionIdLoadVar:
-            return ir_analyze_instruction_load_var(ira, (IrInstructionLoadVar *)instruction);
+        case IrInstructionIdLoadPtr:
+            return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction);
+        case IrInstructionIdStorePtr:
+            zig_panic("TODO store ptr");
+        case IrInstructionIdFieldPtr:
+            zig_panic("TODO field ptr");
+        case IrInstructionIdElemPtr:
+            zig_panic("TODO elem ptr");
+        case IrInstructionIdVarPtr:
+            return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction);
         case IrInstructionIdCall:
             return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction);
         case IrInstructionIdBr:
@@ -3756,7 +3978,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
         case IrInstructionIdPhi:
             return ir_analyze_instruction_phi(ira, (IrInstructionPhi *)instruction);
         case IrInstructionIdSwitchBr:
-        case IrInstructionIdStoreVar:
         case IrInstructionIdCast:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdContainerInitFields:
@@ -3792,8 +4013,8 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
     ira->new_irb.codegen = codegen;
     ira->new_irb.exec = new_exec;
 
-    ira->exec_context.var_slot_count = ira->old_irb.exec->var_slot_count;
-    ira->exec_context.var_slot_list = allocate<IrVarSlot>(ira->exec_context.var_slot_count);
+    ira->exec_context.mem_slot_count = ira->old_irb.exec->mem_slot_count;
+    ira->exec_context.mem_slot_list = allocate<ConstExprValue>(ira->exec_context.mem_slot_count);
 
     TypeTableEntry *return_type = ira->codegen->builtin_types.entry_void;
     for (size_t bb_i = 0; bb_i < ira->old_irb.exec->basic_block_list.length; bb_i += 1) {
@@ -3881,7 +4102,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdCondBr:
         case IrInstructionIdSwitchBr:
         case IrInstructionIdDeclVar:
-        case IrInstructionIdStoreVar:
+        case IrInstructionIdStorePtr:
         case IrInstructionIdCall:
         case IrInstructionIdReturn:
         case IrInstructionIdUnreachable:
@@ -3889,11 +4110,14 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdPhi:
         case IrInstructionIdUnOp:
         case IrInstructionIdBinOp:
-        case IrInstructionIdLoadVar:
+        case IrInstructionIdLoadPtr:
         case IrInstructionIdConst:
         case IrInstructionIdCast:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdContainerInitFields:
+        case IrInstructionIdFieldPtr:
+        case IrInstructionIdElemPtr:
+        case IrInstructionIdVarPtr:
             return false;
         case IrInstructionIdBuiltinCall:
             return ir_builtin_call_has_side_effects((IrInstructionBuiltinCall *)instruction);
src/ir_print.cpp
@@ -21,8 +21,7 @@ static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction) {
     fprintf(irp->f, "#%-3zu| %-12s| %-2s| ", instruction->debug_id, type_name, ref_count);
 }
 
-static void ir_print_const_instruction(IrPrint *irp, IrInstruction *instruction) {
-    TypeTableEntry *type_entry = instruction->type_entry;
+static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, ConstExprValue *const_val) {
     switch (type_entry->id) {
         case TypeTableEntryIdInvalid:
             zig_unreachable();
@@ -30,21 +29,21 @@ static void ir_print_const_instruction(IrPrint *irp, IrInstruction *instruction)
             fprintf(irp->f, "{}");
             break;
         case TypeTableEntryIdNumLitFloat:
-            fprintf(irp->f, "%f", instruction->static_value.data.x_bignum.data.x_float);
+            fprintf(irp->f, "%f", const_val->data.x_bignum.data.x_float);
             break;
         case TypeTableEntryIdNumLitInt:
             {
-                BigNum *bignum = &instruction->static_value.data.x_bignum;
+                BigNum *bignum = &const_val->data.x_bignum;
                 const char *negative_str = bignum->is_negative ? "-" : "";
                 fprintf(irp->f, "%s%llu", negative_str, bignum->data.x_uint);
                 break;
             }
         case TypeTableEntryIdMetaType:
-            fprintf(irp->f, "%s", buf_ptr(&instruction->static_value.data.x_type->name));
+            fprintf(irp->f, "%s", buf_ptr(&const_val->data.x_type->name));
             break;
         case TypeTableEntryIdInt:
             {
-                BigNum *bignum = &instruction->static_value.data.x_bignum;
+                BigNum *bignum = &const_val->data.x_bignum;
                 assert(bignum->kind == BigNumKindInt);
                 const char *negative_str = bignum->is_negative ? "-" : "";
                 fprintf(irp->f, "%s%llu", negative_str, bignum->data.x_uint);
@@ -53,10 +52,18 @@ static void ir_print_const_instruction(IrPrint *irp, IrInstruction *instruction)
         case TypeTableEntryIdUnreachable:
             fprintf(irp->f, "@unreachable()");
             break;
-        case TypeTableEntryIdVar:
         case TypeTableEntryIdBool:
-        case TypeTableEntryIdFloat:
+            {
+                const char *value = const_val->data.x_bool ? "true" : "false";
+                fprintf(irp->f, "%s", value);
+                break;
+            }
         case TypeTableEntryIdPointer:
+            fprintf(irp->f, "&");
+            ir_print_const_value(irp, type_entry->data.pointer.child_type, const_val->data.x_ptr.ptr[0]);
+            break;
+        case TypeTableEntryIdVar:
+        case TypeTableEntryIdFloat:
         case TypeTableEntryIdArray:
         case TypeTableEntryIdStruct:
         case TypeTableEntryIdUndefLit:
@@ -75,6 +82,12 @@ static void ir_print_const_instruction(IrPrint *irp, IrInstruction *instruction)
     }
 }
 
+static void ir_print_const_instruction(IrPrint *irp, IrInstruction *instruction) {
+    TypeTableEntry *type_entry = instruction->type_entry;
+    ConstExprValue *const_val = &instruction->static_value;
+    ir_print_const_value(irp, type_entry, const_val);
+}
+
 static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) {
     if (instruction->static_value.ok) {
         ir_print_const_instruction(irp, instruction);
@@ -211,10 +224,6 @@ static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instr
     ir_print_other_instruction(irp, decl_var_instruction->init_value);
 }
 
-static void ir_print_load_var(IrPrint *irp, IrInstructionLoadVar *load_var_instruction) {
-    fprintf(irp->f, "%s", buf_ptr(&load_var_instruction->var->name));
-}
-
 static void ir_print_cast(IrPrint *irp, IrInstructionCast *cast_instruction) {
     fprintf(irp->f, "cast ");
     ir_print_other_instruction(irp, cast_instruction->value);
@@ -301,9 +310,20 @@ static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruc
     fprintf(irp->f, "unreachable");
 }
 
-static void ir_print_store(IrPrint *irp, IrInstructionStoreVar *store_instruction) {
-    fprintf(irp->f, "%s = ", buf_ptr(&store_instruction->var->name));
-    ir_print_other_instruction(irp, store_instruction->value);
+static void ir_print_elem_ptr(IrPrint *irp, IrInstructionElemPtr *instruction) {
+    ir_print_other_instruction(irp, instruction->array_ptr);
+    fprintf(irp->f, "[");
+    ir_print_other_instruction(irp, instruction->elem_index);
+    fprintf(irp->f, "]");
+}
+
+static void ir_print_var_ptr(IrPrint *irp, IrInstructionVarPtr *instruction) {
+    fprintf(irp->f, "&%s", buf_ptr(&instruction->var->name));
+}
+
+static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) {
+    fprintf(irp->f, "*");
+    ir_print_other_instruction(irp, instruction->ptr);
 }
 
 static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
@@ -323,9 +343,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdDeclVar:
             ir_print_decl_var(irp, (IrInstructionDeclVar *)instruction);
             break;
-        case IrInstructionIdLoadVar:
-            ir_print_load_var(irp, (IrInstructionLoadVar *)instruction);
-            break;
         case IrInstructionIdCast:
             ir_print_cast(irp, (IrInstructionCast *)instruction);
             break;
@@ -356,10 +373,18 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdUnreachable:
             ir_print_unreachable(irp, (IrInstructionUnreachable *)instruction);
             break;
-        case IrInstructionIdStoreVar:
-            ir_print_store(irp, (IrInstructionStoreVar *)instruction);
+        case IrInstructionIdElemPtr:
+            ir_print_elem_ptr(irp, (IrInstructionElemPtr *)instruction);
+            break;
+        case IrInstructionIdVarPtr:
+            ir_print_var_ptr(irp, (IrInstructionVarPtr *)instruction);
+            break;
+        case IrInstructionIdLoadPtr:
+            ir_print_load_ptr(irp, (IrInstructionLoadPtr *)instruction);
             break;
         case IrInstructionIdSwitchBr:
+        case IrInstructionIdStorePtr:
+        case IrInstructionIdFieldPtr:
             zig_panic("TODO print more IR instructions");
     }
     fprintf(irp->f, "\n");
CMakeLists.txt
@@ -38,6 +38,7 @@ include_directories(
 )
 
 set(ZIG_SOURCES
+    "${CMAKE_SOURCE_DIR}/src/ir.cpp"
     "${CMAKE_SOURCE_DIR}/src/analyze.cpp"
     "${CMAKE_SOURCE_DIR}/src/ast_render.cpp"
     "${CMAKE_SOURCE_DIR}/src/bignum.cpp"
@@ -47,7 +48,6 @@ set(ZIG_SOURCES
     "${CMAKE_SOURCE_DIR}/src/errmsg.cpp"
     "${CMAKE_SOURCE_DIR}/src/error.cpp"
     "${CMAKE_SOURCE_DIR}/src/eval.cpp"
-    "${CMAKE_SOURCE_DIR}/src/ir.cpp"
     "${CMAKE_SOURCE_DIR}/src/ir_print.cpp"
     "${CMAKE_SOURCE_DIR}/src/link.cpp"
     "${CMAKE_SOURCE_DIR}/src/main.cpp"