diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/analyze.cpp | 50 | ||||
| -rw-r--r-- | src/ir.cpp | 226 | ||||
| -rw-r--r-- | src/ir.hpp | 153 |
3 files changed, 392 insertions, 37 deletions
diff --git a/src/analyze.cpp b/src/analyze.cpp index 21f82c68ec..c76e546188 100644 --- a/src/analyze.cpp +++ b/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; } diff --git a/src/ir.cpp b/src/ir.cpp new file mode 100644 index 0000000000..ab5d541097 --- /dev/null +++ b/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); + } +} diff --git a/src/ir.hpp b/src/ir.hpp new file mode 100644 index 0000000000..8b7df5c95d --- /dev/null +++ b/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 |
