Commit 4e2fa2d15b

Andrew Kelley <superjoe30@gmail.com>
2016-09-30 08:21:21
*WIP*
1 parent 0562111
src/analyze.cpp
@@ -6,14 +6,15 @@
  */
 
 #include "analyze.hpp"
-#include "parser.hpp"
+#include "ast_render.hpp"
+#include "config.h"
 #include "error.hpp"
-#include "zig_llvm.hpp"
+#include "eval.hpp"
+#include "ir.hpp"
 #include "os.hpp"
 #include "parseh.hpp"
-#include "config.h"
-#include "ast_render.hpp"
-#include "eval.hpp"
+#include "parser.hpp"
+#include "zig_llvm.hpp"
 
 static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node);
@@ -6880,22 +6881,6 @@ static TypeTableEntry *analyze_goto_pass1(CodeGen *g, ImportTableEntry *import,
     return g->builtin_types.entry_unreachable;
 }
 
-static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) {
-    assert(node->type == NodeTypeGoto);
-    Buf *label_name = node->data.goto_expr.name;
-    BlockContext *context = node->block_context;
-    assert(context);
-    LabelTableEntry *label = find_label(g, context, label_name);
-
-    if (!label) {
-        add_node_error(g, node, buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
-        return;
-    }
-
-    label->used = true;
-    node->data.goto_expr.label_entry = label;
-}
-
 static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import,
         BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only)
 {
@@ -7113,24 +7098,15 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
             buf_sprintf("byvalue types not yet supported on extern function return values"));
     }
 
-    TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);
-
-    node->data.fn_def.implicit_return_type = block_return_type;
-
-    for (size_t i = 0; i < fn_table_entry->goto_list.length; i += 1) {
-        AstNode *goto_node = fn_table_entry->goto_list.at(i);
-        assert(goto_node->type == NodeTypeGoto);
-        analyze_goto_pass2(g, import, goto_node);
+    IrBasicBlock *entry_basic_block = ir_gen(g, node, expected_type);
+    if (!entry_basic_block) {
+        fn_proto_node->data.fn_proto.skip = true;
+        fn_table_entry->anal_state = FnAnalStateSkipped;
+        return;
     }
+    TypeTableEntry *block_return_type = ir_analyze(g, node, entry_basic_block, expected_type);
 
-    for (size_t i = 0; i < fn_table_entry->all_labels.length; i += 1) {
-        LabelTableEntry *label = fn_table_entry->all_labels.at(i);
-        if (!label->used) {
-            add_node_error(g, label->decl_node,
-                    buf_sprintf("label '%s' defined but not used",
-                        buf_ptr(label->decl_node->data.label.name)));
-        }
-    }
+    node->data.fn_def.implicit_return_type = block_return_type;
 
     fn_table_entry->anal_state = FnAnalStateComplete;
 }
src/ir.cpp
@@ -0,0 +1,226 @@
+#include "analyze.hpp"
+#include "ir.hpp"
+
+static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_context);
+
+static const IrInstruction invalid_instruction_data;
+static const IrInstruction *invalid_instruction = &invalid_instruction_data;
+
+static const IrInstruction void_instruction_data;
+static const IrInstruction *void_instruction = &void_instruction_data;
+
+struct IrGen {
+    CodeGen *codegen;
+    AstNode *fn_def_node;
+    IrBasicBlock *current_basic_block;
+};
+
+static IrInstruction *ir_build_return(Ir *ir, AstNode *source_node, IrInstruction *return_value) {
+    IrInstruction *instructon = allocate<IrInstructionReturn>(1);
+    instruction->base.id = IrInstructionIdReturn;
+    instruction->base.source_node = source_node;
+    instruction->base.type_entry = ir->codegen->builtin_types.entry_unreachable;
+    ir->current_basic_block->instructions->append(instruction);
+    return instructon;
+}
+
+static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
+    size_t result = 0;
+    while (inner_block != outer_block) {
+        if (inner_block->node->type == NodeTypeDefer &&
+           (inner_block->node->data.defer.kind == ReturnKindError ||
+            inner_block->node->data.defer.kind == ReturnKindMaybe))
+        {
+            result += 1;
+        }
+        inner_block = inner_block->parent;
+    }
+    return result;
+}
+
+static void ir_gen_defers_for_block(Ir *ir, BlockContext *inner_block, BlockContext *outer_block,
+        bool gen_error_defers, bool gen_maybe_defers)
+{
+    while (inner_block != outer_block) {
+        if (inner_block->node->type == NodeTypeDefer &&
+           ((inner_block->node->data.defer.kind == ReturnKindUnconditional) ||
+            (gen_error_defers && inner_block->node->data.defer.kind == ReturnKindError) ||
+            (gen_maybe_defers && inner_block->node->data.defer.kind == ReturnKindMaybe)))
+        {
+            AstNode *defer_expr_node = inner_block->node->data.defer.expr;
+            ir_gen_node(ir, defer_expr_node, defer_expr_node->block_context);
+        }
+        inner_block = inner_block->parent;
+    }
+}
+
+static IrInstruction *ir_gen_return(Ir *ir, AstNode *source_node, IrInstruction *value, ReturnKnowledge rk) {
+    BlockContext *defer_inner_block = source_node->block_context;
+    BlockContext *defer_outer_block = ir->fn_def_node->block_context;
+    if (rk == ReturnKnowledgeUnknown) {
+        if (get_conditional_defer_count(defer_inner_block, defer_outer_block) > 0) {
+            // generate branching code that checks the return value and generates defers
+            // if the return value is error
+            zig_panic("TODO");
+        }
+    } else if (rk != ReturnKnowledgeSkipDefers) {
+        ir_gen_defers_for_block(g, defer_inner_block, defer_outer_block,
+                rk == ReturnKnowledgeKnownError, rk == ReturnKnowledgeKnownNull);
+    }
+
+    ir_build_return(ir, source_node, value);
+    return void_instruction;
+}
+
+static IrInstruction *ir_gen_block(IrGen *ir, AstNode *block_node, TypeTableEntry *implicit_return_type) {
+    assert(block_node->type == NodeTypeBlock);
+
+    BlockContext *parent_context = block_node->context;
+    BlockContext *outer_block_context = new_block_context(block_node, parent_context);
+    BlockContext *child_context = outer_block_context;
+
+    IrInstruction *return_value = nullptr;
+    for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) {
+        AstNode *statement_node = block_node->data.block.statements.at(i);
+        return_value = ir_gen_node(g, statement_node, child_context);
+        if (statement_node->type == NodeTypeDefer && return_value != invalid_instruction) {
+            // defer starts a new block context
+            child_context = statement_node->data.defer.child_block;
+            assert(child_context);
+        }
+    }
+
+    ir_gen_defers_for_block(ir, child_context, outer_block_context, false, false);
+
+    return return_value;
+}
+
+static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_context) {
+    node->block_context = block_context;
+
+    switch (node->type) {
+        case NodeTypeBlock:
+            return ir_gen_block(ir, node, nullptr);
+        case NodeTypeBinOpExpr:
+        case NodeTypeUnwrapErrorExpr:
+        case NodeTypeReturnExpr:
+        case NodeTypeDefer:
+        case NodeTypeVariableDeclaration:
+        case NodeTypePrefixOpExpr:
+        case NodeTypeFnCallExpr:
+        case NodeTypeArrayAccessExpr:
+        case NodeTypeSliceExpr:
+        case NodeTypeFieldAccessExpr:
+        case NodeTypeIfBoolExpr:
+        case NodeTypeIfVarExpr:
+        case NodeTypeWhileExpr:
+        case NodeTypeForExpr:
+        case NodeTypeAsmExpr:
+        case NodeTypeSymbol:
+        case NodeTypeGoto:
+        case NodeTypeBreak:
+        case NodeTypeContinue:
+        case NodeTypeLabel:
+        case NodeTypeContainerInitExpr:
+        case NodeTypeSwitchExpr:
+        case NodeTypeNumberLiteral:
+        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_panic("TODO more IR gen");
+    }
+    zig_unreachable();
+}
+
+IrBasicBlock *ir_gen(CodeGen *g, AstNode *fn_def_node, TypeTableEntry *return_type) {
+    assert(fn_def_node->type == NodeTypeFnDef);
+    assert(fn_def_node->data.fn_def.block_context);
+    assert(fn_def_node->owner);
+    assert(return_type);
+    assert(return_type->id != TypeTableEntryIdInvalid);
+
+    IrGen ir_gen = {0};
+    IrGen *ir = &ir_gen;
+
+    IrBasicBlock *entry_basic_block = allocate<IrBasicBlock>(1);
+    ir->current_basic_block = entry_basic_block;
+
+    AstNode *body_node = fn_def_node->data.fn_def.body;
+    body_node->block_context = fn_def_node->data.fn_def.block_context;
+    IrInstruction *instruction = ir_gen_block(ir, body_node, return_type);
+    return (instructon == invalid_instruction) ? nullptr : entry_basic_block;
+}
+
+/*
+static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) {
+    assert(node->type == NodeTypeGoto);
+    Buf *label_name = node->data.goto_expr.name;
+    BlockContext *context = node->block_context;
+    assert(context);
+    LabelTableEntry *label = find_label(g, context, label_name);
+
+    if (!label) {
+        add_node_error(g, node, buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
+        return;
+    }
+
+    label->used = true;
+    node->data.goto_expr.label_entry = label;
+}
+
+    for (size_t i = 0; i < fn_table_entry->goto_list.length; i += 1) {
+        AstNode *goto_node = fn_table_entry->goto_list.at(i);
+        assert(goto_node->type == NodeTypeGoto);
+        analyze_goto_pass2(g, import, goto_node);
+    }
+
+    for (size_t i = 0; i < fn_table_entry->all_labels.length; i += 1) {
+        LabelTableEntry *label = fn_table_entry->all_labels.at(i);
+        if (!label->used) {
+            add_node_error(g, label->decl_node,
+                    buf_sprintf("label '%s' defined but not used",
+                        buf_ptr(label->decl_node->data.label.name)));
+        }
+    }
+*/
+
+
+TypeTableEntry *ir_analyze(CodeGen *g, AstNode *fn_def_node, IrBasicBlock *entry_basic_block,
+        TypeTableEntry *expected_type)
+{
+    TypeTableEntry *return_type = g->builtin_types.entry_void;
+
+    for (size_t i = 0; i < entry_basic_block->instructions.length; i += 1) {
+        IrInstruction *instruction = entry_basic_block->instructions.at(i);
+
+        if (return_type->id == TypeTableEntryIdUnreachable) {
+            add_node_error(g, first_executing_node(instruction->source_node),
+                    buf_sprintf("unreachable code"));
+            break;
+        }
+        bool is_last = (i == entry_basic_block->instructions.length - 1);
+        TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr;
+        return_type = ir_analyze_instruction(g, instruction, passed_expected_type, child);
+    }
+}
src/ir.hpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2016 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_IR_HPP
+#define ZIG_IR_HPP
+
+#include "all_types.hpp"
+
+struct IrInstruction;
+
+// A basic block contains no branching. Branches send control flow
+// to another basic block.
+// Phi instructions must be first in a basic block.
+// The last instruction in a basic block must be an expression of type unreachable.
+struct IrBasicBlock {
+    ZigList<IrInstruction *> instructions;
+};
+
+enum IrInstructionId {
+    IrInstructionIdCondBr,
+    IrInstructionIdSwitchBr,
+    IrInstructionIdPhi,
+    IrInstructionIdAdd,
+    IrInstructionIdBinOp,
+    IrInstructionIdLoadVar,
+    IrInstructionIdStoreVar,
+    IrInstructionIdCall,
+    IrInstructionIdBuiltinCall,
+    IrInstructionIdConst,
+    IrInstructionIdReturn,
+};
+
+struct IrInstruction {
+    IrInstructionId id;
+    AstNode *source_node;
+    ConstExprValue static_value;
+    TypeTableEntry *type_entry;
+};
+
+struct IrInstructionCondBr {
+    IrInstruction base;
+
+    // If the condition is null, then this is an unconditional branch.
+    IrInstruction *cond;
+    IrBasicBlock *dest;
+};
+
+struct IrInstructionSwitchBrCase {
+    IrInstruction *value;
+    IrBasicBlock *block;
+};
+
+struct IrInstructionSwitchBr {
+    IrInstruction base;
+
+    IrInstruction *target_value;
+    IrBasicBlock *else_block;
+    size_t case_count;
+    IrInstructionSwitchBrCase *cases;
+};
+
+struct IrInstructionPhi {
+    IrInstruction base;
+
+    size_t incoming_block_count;
+    IrBasicBlock **incoming_blocks;
+    IrInstruction **incoming_values;
+};
+
+enum IrBinOp {
+    IrBinOpInvalid,
+    IrBinOpBoolOr,
+    IrBinOpBoolAnd,
+    IrBinOpCmpEq,
+    IrBinOpCmpNotEq,
+    IrBinOpCmpLessThan,
+    IrBinOpCmpGreaterThan,
+    IrBinOpCmpLessOrEq,
+    IrBinOpCmpGreaterOrEq,
+    IrBinOpBinOr,
+    IrBinOpBinXor,
+    IrBinOpBinAnd,
+    IrBinOpBitShiftLeft,
+    IrBinOpBitShiftLeftWrap,
+    IrBinOpBitShiftRight,
+    IrBinOpAdd,
+    IrBinOpAddWrap,
+    IrBinOpSub,
+    IrBinOpSubWrap,
+    IrBinOpMult,
+    IrBinOpMultWrap,
+    IrBinOpDiv,
+    IrBinOpMod,
+    IrBinOpArrayCat,
+    IrBinOpArrayMult,
+};
+
+struct IrInstructionBinOp {
+    IrInstruction base;
+
+    IrInstruction *op1;
+    IrBinOp op_id;
+    IrInstruction *op2;
+};
+
+struct IrInstructionLoadVar {
+    IrInstruction base;
+
+    VariableTableEntry *var;
+};
+
+struct IrInstructionStoreVar {
+    IrInstruction base;
+
+    IrInstruction *value;
+    VariableTableEntry *var;
+};
+
+struct IrInstructionCall {
+    IrInstruction base;
+
+    IrInstruction *fn;
+    size_t arg_count;
+    IrInstruction **args;
+};
+
+struct IrInstructionBuiltinCall {
+    IrInstruction base;
+
+    BuiltinFnId fn_id;
+    size_t arg_count;
+    IrInstruction **args;
+};
+
+struct IrInstructionConst {
+    IrInstruction base;
+};
+
+struct IrInstructionReturn {
+    IrInstruction base;
+
+    IrInstruction *value;
+};
+
+IrBasicBlock *ir_gen(CodeGen *g, AstNode *fn_def_node, TypeTableEntry *return_type);
+TypeTableEntry *ir_analyze(CodeGen *g, AstNode *fn_def_node, IrBasicBlock *entry_basic_block,
+        TypeTableEntry *expected_type);
+
+#endif
CMakeLists.txt
@@ -38,24 +38,25 @@ include_directories(
 )
 
 set(ZIG_SOURCES
-    "${CMAKE_SOURCE_DIR}/src/link.cpp"
-    "${CMAKE_SOURCE_DIR}/src/target.cpp"
+    "${CMAKE_SOURCE_DIR}/src/analyze.cpp"
     "${CMAKE_SOURCE_DIR}/src/ast_render.cpp"
     "${CMAKE_SOURCE_DIR}/src/bignum.cpp"
-    "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
+    "${CMAKE_SOURCE_DIR}/src/buffer.cpp"
     "${CMAKE_SOURCE_DIR}/src/c_tokenizer.cpp"
-    "${CMAKE_SOURCE_DIR}/src/parser.cpp"
-    "${CMAKE_SOURCE_DIR}/src/eval.cpp"
-    "${CMAKE_SOURCE_DIR}/src/analyze.cpp"
     "${CMAKE_SOURCE_DIR}/src/codegen.cpp"
-    "${CMAKE_SOURCE_DIR}/src/buffer.cpp"
+    "${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/link.cpp"
     "${CMAKE_SOURCE_DIR}/src/main.cpp"
     "${CMAKE_SOURCE_DIR}/src/os.cpp"
+    "${CMAKE_SOURCE_DIR}/src/parseh.cpp"
+    "${CMAKE_SOURCE_DIR}/src/parser.cpp"
+    "${CMAKE_SOURCE_DIR}/src/target.cpp"
+    "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
     "${CMAKE_SOURCE_DIR}/src/util.cpp"
-    "${CMAKE_SOURCE_DIR}/src/errmsg.cpp"
     "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
-    "${CMAKE_SOURCE_DIR}/src/parseh.cpp"
 )
 
 set(TEST_SOURCES