Commit 4e2fa2d15b
Changed files (4)
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