| author | |
| committer | |
| log | 4e2fa2d15be248c29051a58995e38caa0b1de0a5 |
| tree | b8f27f8f3157b1897cf796c6b0241dafacbfd7b0 |
| parent | 0562111b0208d94852928f4bc7a7f2b8e335366f |
4 files changed, 402 insertions(+), 46 deletions(-)
CMakeLists.txt+10-9| ... | ... | @@ -38,24 +38,25 @@ include_directories( |
| 38 | 38 | ) |
| 39 | 39 | |
| 40 | 40 | set(ZIG_SOURCES |
| 41 | "${CMAKE_SOURCE_DIR}/src/link.cpp" | |
| 42 | "${CMAKE_SOURCE_DIR}/src/target.cpp" | |
| 41 | "${CMAKE_SOURCE_DIR}/src/analyze.cpp" | |
| 43 | 42 | "${CMAKE_SOURCE_DIR}/src/ast_render.cpp" |
| 44 | 43 | "${CMAKE_SOURCE_DIR}/src/bignum.cpp" |
| 45 | "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp" | |
| 44 | "${CMAKE_SOURCE_DIR}/src/buffer.cpp" | |
| 46 | 45 | "${CMAKE_SOURCE_DIR}/src/c_tokenizer.cpp" |
| 47 | "${CMAKE_SOURCE_DIR}/src/parser.cpp" | |
| 48 | "${CMAKE_SOURCE_DIR}/src/eval.cpp" | |
| 49 | "${CMAKE_SOURCE_DIR}/src/analyze.cpp" | |
| 50 | 46 | "${CMAKE_SOURCE_DIR}/src/codegen.cpp" |
| 51 | "${CMAKE_SOURCE_DIR}/src/buffer.cpp" | |
| 47 | "${CMAKE_SOURCE_DIR}/src/errmsg.cpp" | |
| 52 | 48 | "${CMAKE_SOURCE_DIR}/src/error.cpp" |
| 49 | "${CMAKE_SOURCE_DIR}/src/eval.cpp" | |
| 50 | "${CMAKE_SOURCE_DIR}/src/ir.cpp" | |
| 51 | "${CMAKE_SOURCE_DIR}/src/link.cpp" | |
| 53 | 52 | "${CMAKE_SOURCE_DIR}/src/main.cpp" |
| 54 | 53 | "${CMAKE_SOURCE_DIR}/src/os.cpp" |
| 54 | "${CMAKE_SOURCE_DIR}/src/parseh.cpp" | |
| 55 | "${CMAKE_SOURCE_DIR}/src/parser.cpp" | |
| 56 | "${CMAKE_SOURCE_DIR}/src/target.cpp" | |
| 57 | "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp" | |
| 55 | 58 | "${CMAKE_SOURCE_DIR}/src/util.cpp" |
| 56 | "${CMAKE_SOURCE_DIR}/src/errmsg.cpp" | |
| 57 | 59 | "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" |
| 58 | "${CMAKE_SOURCE_DIR}/src/parseh.cpp" | |
| 59 | 60 | ) |
| 60 | 61 | |
| 61 | 62 | set(TEST_SOURCES |
src/analyze.cpp+13-37| ... | ... | @@ -6,14 +6,15 @@ |
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | 8 | #include "analyze.hpp" |
| 9 | #include "parser.hpp" | |
| 9 | #include "ast_render.hpp" | |
| 10 | #include "config.h" | |
| 10 | 11 | #include "error.hpp" |
| 11 | #include "zig_llvm.hpp" | |
| 12 | #include "eval.hpp" | |
| 13 | #include "ir.hpp" | |
| 12 | 14 | #include "os.hpp" |
| 13 | 15 | #include "parseh.hpp" |
| 14 | #include "config.h" | |
| 15 | #include "ast_render.hpp" | |
| 16 | #include "eval.hpp" | |
| 16 | #include "parser.hpp" | |
| 17 | #include "zig_llvm.hpp" | |
| 17 | 18 | |
| 18 | 19 | static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 19 | 20 | TypeTableEntry *expected_type, AstNode *node); |
| ... | ... | @@ -6880,22 +6881,6 @@ static TypeTableEntry *analyze_goto_pass1(CodeGen *g, ImportTableEntry *import, |
| 6880 | 6881 | return g->builtin_types.entry_unreachable; |
| 6881 | 6882 | } |
| 6882 | 6883 | |
| 6883 | static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) { | |
| 6884 | assert(node->type == NodeTypeGoto); | |
| 6885 | Buf *label_name = node->data.goto_expr.name; | |
| 6886 | BlockContext *context = node->block_context; | |
| 6887 | assert(context); | |
| 6888 | LabelTableEntry *label = find_label(g, context, label_name); | |
| 6889 | ||
| 6890 | if (!label) { | |
| 6891 | add_node_error(g, node, buf_sprintf("no label in scope named '%s'", buf_ptr(label_name))); | |
| 6892 | return; | |
| 6893 | } | |
| 6894 | ||
| 6895 | label->used = true; | |
| 6896 | node->data.goto_expr.label_entry = label; | |
| 6897 | } | |
| 6898 | ||
| 6899 | 6884 | static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import, |
| 6900 | 6885 | BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only) |
| 6901 | 6886 | { |
| ... | ... | @@ -7113,24 +7098,15 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 7113 | 7098 | buf_sprintf("byvalue types not yet supported on extern function return values")); |
| 7114 | 7099 | } |
| 7115 | 7100 | |
| 7116 | TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body); | |
| 7117 | ||
| 7118 | node->data.fn_def.implicit_return_type = block_return_type; | |
| 7119 | ||
| 7120 | for (size_t i = 0; i < fn_table_entry->goto_list.length; i += 1) { | |
| 7121 | AstNode *goto_node = fn_table_entry->goto_list.at(i); | |
| 7122 | assert(goto_node->type == NodeTypeGoto); | |
| 7123 | analyze_goto_pass2(g, import, goto_node); | |
| 7101 | IrBasicBlock *entry_basic_block = ir_gen(g, node, expected_type); | |
| 7102 | if (!entry_basic_block) { | |
| 7103 | fn_proto_node->data.fn_proto.skip = true; | |
| 7104 | fn_table_entry->anal_state = FnAnalStateSkipped; | |
| 7105 | return; | |
| 7124 | 7106 | } |
| 7107 | TypeTableEntry *block_return_type = ir_analyze(g, node, entry_basic_block, expected_type); | |
| 7125 | 7108 | |
| 7126 | for (size_t i = 0; i < fn_table_entry->all_labels.length; i += 1) { | |
| 7127 | LabelTableEntry *label = fn_table_entry->all_labels.at(i); | |
| 7128 | if (!label->used) { | |
| 7129 | add_node_error(g, label->decl_node, | |
| 7130 | buf_sprintf("label '%s' defined but not used", | |
| 7131 | buf_ptr(label->decl_node->data.label.name))); | |
| 7132 | } | |
| 7133 | } | |
| 7109 | node->data.fn_def.implicit_return_type = block_return_type; | |
| 7134 | 7110 | |
| 7135 | 7111 | fn_table_entry->anal_state = FnAnalStateComplete; |
| 7136 | 7112 | } |
src/ir.cpp created+226| ... | ... | @@ -0,0 +1,226 @@ |
| 1 | #include "analyze.hpp" | |
| 2 | #include "ir.hpp" | |
| 3 | ||
| 4 | static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_context); | |
| 5 | ||
| 6 | static const IrInstruction invalid_instruction_data; | |
| 7 | static const IrInstruction *invalid_instruction = &invalid_instruction_data; | |
| 8 | ||
| 9 | static const IrInstruction void_instruction_data; | |
| 10 | static const IrInstruction *void_instruction = &void_instruction_data; | |
| 11 | ||
| 12 | struct IrGen { | |
| 13 | CodeGen *codegen; | |
| 14 | AstNode *fn_def_node; | |
| 15 | IrBasicBlock *current_basic_block; | |
| 16 | }; | |
| 17 | ||
| 18 | static IrInstruction *ir_build_return(Ir *ir, AstNode *source_node, IrInstruction *return_value) { | |
| 19 | IrInstruction *instructon = allocate<IrInstructionReturn>(1); | |
| 20 | instruction->base.id = IrInstructionIdReturn; | |
| 21 | instruction->base.source_node = source_node; | |
| 22 | instruction->base.type_entry = ir->codegen->builtin_types.entry_unreachable; | |
| 23 | ir->current_basic_block->instructions->append(instruction); | |
| 24 | return instructon; | |
| 25 | } | |
| 26 | ||
| 27 | static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) { | |
| 28 | size_t result = 0; | |
| 29 | while (inner_block != outer_block) { | |
| 30 | if (inner_block->node->type == NodeTypeDefer && | |
| 31 | (inner_block->node->data.defer.kind == ReturnKindError || | |
| 32 | inner_block->node->data.defer.kind == ReturnKindMaybe)) | |
| 33 | { | |
| 34 | result += 1; | |
| 35 | } | |
| 36 | inner_block = inner_block->parent; | |
| 37 | } | |
| 38 | return result; | |
| 39 | } | |
| 40 | ||
| 41 | static void ir_gen_defers_for_block(Ir *ir, BlockContext *inner_block, BlockContext *outer_block, | |
| 42 | bool gen_error_defers, bool gen_maybe_defers) | |
| 43 | { | |
| 44 | while (inner_block != outer_block) { | |
| 45 | if (inner_block->node->type == NodeTypeDefer && | |
| 46 | ((inner_block->node->data.defer.kind == ReturnKindUnconditional) || | |
| 47 | (gen_error_defers && inner_block->node->data.defer.kind == ReturnKindError) || | |
| 48 | (gen_maybe_defers && inner_block->node->data.defer.kind == ReturnKindMaybe))) | |
| 49 | { | |
| 50 | AstNode *defer_expr_node = inner_block->node->data.defer.expr; | |
| 51 | ir_gen_node(ir, defer_expr_node, defer_expr_node->block_context); | |
| 52 | } | |
| 53 | inner_block = inner_block->parent; | |
| 54 | } | |
| 55 | } | |
| 56 | ||
| 57 | static IrInstruction *ir_gen_return(Ir *ir, AstNode *source_node, IrInstruction *value, ReturnKnowledge rk) { | |
| 58 | BlockContext *defer_inner_block = source_node->block_context; | |
| 59 | BlockContext *defer_outer_block = ir->fn_def_node->block_context; | |
| 60 | if (rk == ReturnKnowledgeUnknown) { | |
| 61 | if (get_conditional_defer_count(defer_inner_block, defer_outer_block) > 0) { | |
| 62 | // generate branching code that checks the return value and generates defers | |
| 63 | // if the return value is error | |
| 64 | zig_panic("TODO"); | |
| 65 | } | |
| 66 | } else if (rk != ReturnKnowledgeSkipDefers) { | |
| 67 | ir_gen_defers_for_block(g, defer_inner_block, defer_outer_block, | |
| 68 | rk == ReturnKnowledgeKnownError, rk == ReturnKnowledgeKnownNull); | |
| 69 | } | |
| 70 | ||
| 71 | ir_build_return(ir, source_node, value); | |
| 72 | return void_instruction; | |
| 73 | } | |
| 74 | ||
| 75 | static IrInstruction *ir_gen_block(IrGen *ir, AstNode *block_node, TypeTableEntry *implicit_return_type) { | |
| 76 | assert(block_node->type == NodeTypeBlock); | |
| 77 | ||
| 78 | BlockContext *parent_context = block_node->context; | |
| 79 | BlockContext *outer_block_context = new_block_context(block_node, parent_context); | |
| 80 | BlockContext *child_context = outer_block_context; | |
| 81 | ||
| 82 | IrInstruction *return_value = nullptr; | |
| 83 | for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) { | |
| 84 | AstNode *statement_node = block_node->data.block.statements.at(i); | |
| 85 | return_value = ir_gen_node(g, statement_node, child_context); | |
| 86 | if (statement_node->type == NodeTypeDefer && return_value != invalid_instruction) { | |
| 87 | // defer starts a new block context | |
| 88 | child_context = statement_node->data.defer.child_block; | |
| 89 | assert(child_context); | |
| 90 | } | |
| 91 | } | |
| 92 | ||
| 93 | ir_gen_defers_for_block(ir, child_context, outer_block_context, false, false); | |
| 94 | ||
| 95 | return return_value; | |
| 96 | } | |
| 97 | ||
| 98 | static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_context) { | |
| 99 | node->block_context = block_context; | |
| 100 | ||
| 101 | switch (node->type) { | |
| 102 | case NodeTypeBlock: | |
| 103 | return ir_gen_block(ir, node, nullptr); | |
| 104 | case NodeTypeBinOpExpr: | |
| 105 | case NodeTypeUnwrapErrorExpr: | |
| 106 | case NodeTypeReturnExpr: | |
| 107 | case NodeTypeDefer: | |
| 108 | case NodeTypeVariableDeclaration: | |
| 109 | case NodeTypePrefixOpExpr: | |
| 110 | case NodeTypeFnCallExpr: | |
| 111 | case NodeTypeArrayAccessExpr: | |
| 112 | case NodeTypeSliceExpr: | |
| 113 | case NodeTypeFieldAccessExpr: | |
| 114 | case NodeTypeIfBoolExpr: | |
| 115 | case NodeTypeIfVarExpr: | |
| 116 | case NodeTypeWhileExpr: | |
| 117 | case NodeTypeForExpr: | |
| 118 | case NodeTypeAsmExpr: | |
| 119 | case NodeTypeSymbol: | |
| 120 | case NodeTypeGoto: | |
| 121 | case NodeTypeBreak: | |
| 122 | case NodeTypeContinue: | |
| 123 | case NodeTypeLabel: | |
| 124 | case NodeTypeContainerInitExpr: | |
| 125 | case NodeTypeSwitchExpr: | |
| 126 | case NodeTypeNumberLiteral: | |
| 127 | case NodeTypeBoolLiteral: | |
| 128 | case NodeTypeStringLiteral: | |
| 129 | case NodeTypeCharLiteral: | |
| 130 | case NodeTypeNullLiteral: | |
| 131 | case NodeTypeUndefinedLiteral: | |
| 132 | case NodeTypeZeroesLiteral: | |
| 133 | case NodeTypeThisLiteral: | |
| 134 | case NodeTypeErrorType: | |
| 135 | case NodeTypeTypeLiteral: | |
| 136 | case NodeTypeArrayType: | |
| 137 | case NodeTypeVarLiteral: | |
| 138 | case NodeTypeRoot: | |
| 139 | case NodeTypeFnProto: | |
| 140 | case NodeTypeFnDef: | |
| 141 | case NodeTypeFnDecl: | |
| 142 | case NodeTypeParamDecl: | |
| 143 | case NodeTypeUse: | |
| 144 | case NodeTypeContainerDecl: | |
| 145 | case NodeTypeStructField: | |
| 146 | case NodeTypeStructValueField: | |
| 147 | case NodeTypeSwitchProng: | |
| 148 | case NodeTypeSwitchRange: | |
| 149 | case NodeTypeErrorValueDecl: | |
| 150 | case NodeTypeTypeDecl: | |
| 151 | zig_panic("TODO more IR gen"); | |
| 152 | } | |
| 153 | zig_unreachable(); | |
| 154 | } | |
| 155 | ||
| 156 | IrBasicBlock *ir_gen(CodeGen *g, AstNode *fn_def_node, TypeTableEntry *return_type) { | |
| 157 | assert(fn_def_node->type == NodeTypeFnDef); | |
| 158 | assert(fn_def_node->data.fn_def.block_context); | |
| 159 | assert(fn_def_node->owner); | |
| 160 | assert(return_type); | |
| 161 | assert(return_type->id != TypeTableEntryIdInvalid); | |
| 162 | ||
| 163 | IrGen ir_gen = {0}; | |
| 164 | IrGen *ir = &ir_gen; | |
| 165 | ||
| 166 | IrBasicBlock *entry_basic_block = allocate<IrBasicBlock>(1); | |
| 167 | ir->current_basic_block = entry_basic_block; | |
| 168 | ||
| 169 | AstNode *body_node = fn_def_node->data.fn_def.body; | |
| 170 | body_node->block_context = fn_def_node->data.fn_def.block_context; | |
| 171 | IrInstruction *instruction = ir_gen_block(ir, body_node, return_type); | |
| 172 | return (instructon == invalid_instruction) ? nullptr : entry_basic_block; | |
| 173 | } | |
| 174 | ||
| 175 | /* | |
| 176 | static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) { | |
| 177 | assert(node->type == NodeTypeGoto); | |
| 178 | Buf *label_name = node->data.goto_expr.name; | |
| 179 | BlockContext *context = node->block_context; | |
| 180 | assert(context); | |
| 181 | LabelTableEntry *label = find_label(g, context, label_name); | |
| 182 | ||
| 183 | if (!label) { | |
| 184 | add_node_error(g, node, buf_sprintf("no label in scope named '%s'", buf_ptr(label_name))); | |
| 185 | return; | |
| 186 | } | |
| 187 | ||
| 188 | label->used = true; | |
| 189 | node->data.goto_expr.label_entry = label; | |
| 190 | } | |
| 191 | ||
| 192 | for (size_t i = 0; i < fn_table_entry->goto_list.length; i += 1) { | |
| 193 | AstNode *goto_node = fn_table_entry->goto_list.at(i); | |
| 194 | assert(goto_node->type == NodeTypeGoto); | |
| 195 | analyze_goto_pass2(g, import, goto_node); | |
| 196 | } | |
| 197 | ||
| 198 | for (size_t i = 0; i < fn_table_entry->all_labels.length; i += 1) { | |
| 199 | LabelTableEntry *label = fn_table_entry->all_labels.at(i); | |
| 200 | if (!label->used) { | |
| 201 | add_node_error(g, label->decl_node, | |
| 202 | buf_sprintf("label '%s' defined but not used", | |
| 203 | buf_ptr(label->decl_node->data.label.name))); | |
| 204 | } | |
| 205 | } | |
| 206 | */ | |
| 207 | ||
| 208 | ||
| 209 | TypeTableEntry *ir_analyze(CodeGen *g, AstNode *fn_def_node, IrBasicBlock *entry_basic_block, | |
| 210 | TypeTableEntry *expected_type) | |
| 211 | { | |
| 212 | TypeTableEntry *return_type = g->builtin_types.entry_void; | |
| 213 | ||
| 214 | for (size_t i = 0; i < entry_basic_block->instructions.length; i += 1) { | |
| 215 | IrInstruction *instruction = entry_basic_block->instructions.at(i); | |
| 216 | ||
| 217 | if (return_type->id == TypeTableEntryIdUnreachable) { | |
| 218 | add_node_error(g, first_executing_node(instruction->source_node), | |
| 219 | buf_sprintf("unreachable code")); | |
| 220 | break; | |
| 221 | } | |
| 222 | bool is_last = (i == entry_basic_block->instructions.length - 1); | |
| 223 | TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; | |
| 224 | return_type = ir_analyze_instruction(g, instruction, passed_expected_type, child); | |
| 225 | } | |
| 226 | } |
src/ir.hpp created+153| ... | ... | @@ -0,0 +1,153 @@ |
| 1 | /* | |
| 2 | * Copyright (c) 2016 Andrew Kelley | |
| 3 | * | |
| 4 | * This file is part of zig, which is MIT licensed. | |
| 5 | * See http://opensource.org/licenses/MIT | |
| 6 | */ | |
| 7 | ||
| 8 | #ifndef ZIG_IR_HPP | |
| 9 | #define ZIG_IR_HPP | |
| 10 | ||
| 11 | #include "all_types.hpp" | |
| 12 | ||
| 13 | struct IrInstruction; | |
| 14 | ||
| 15 | // A basic block contains no branching. Branches send control flow | |
| 16 | // to another basic block. | |
| 17 | // Phi instructions must be first in a basic block. | |
| 18 | // The last instruction in a basic block must be an expression of type unreachable. | |
| 19 | struct IrBasicBlock { | |
| 20 | ZigList<IrInstruction *> instructions; | |
| 21 | }; | |
| 22 | ||
| 23 | enum IrInstructionId { | |
| 24 | IrInstructionIdCondBr, | |
| 25 | IrInstructionIdSwitchBr, | |
| 26 | IrInstructionIdPhi, | |
| 27 | IrInstructionIdAdd, | |
| 28 | IrInstructionIdBinOp, | |
| 29 | IrInstructionIdLoadVar, | |
| 30 | IrInstructionIdStoreVar, | |
| 31 | IrInstructionIdCall, | |
| 32 | IrInstructionIdBuiltinCall, | |
| 33 | IrInstructionIdConst, | |
| 34 | IrInstructionIdReturn, | |
| 35 | }; | |
| 36 | ||
| 37 | struct IrInstruction { | |
| 38 | IrInstructionId id; | |
| 39 | AstNode *source_node; | |
| 40 | ConstExprValue static_value; | |
| 41 | TypeTableEntry *type_entry; | |
| 42 | }; | |
| 43 | ||
| 44 | struct IrInstructionCondBr { | |
| 45 | IrInstruction base; | |
| 46 | ||
| 47 | // If the condition is null, then this is an unconditional branch. | |
| 48 | IrInstruction *cond; | |
| 49 | IrBasicBlock *dest; | |
| 50 | }; | |
| 51 | ||
| 52 | struct IrInstructionSwitchBrCase { | |
| 53 | IrInstruction *value; | |
| 54 | IrBasicBlock *block; | |
| 55 | }; | |
| 56 | ||
| 57 | struct IrInstructionSwitchBr { | |
| 58 | IrInstruction base; | |
| 59 | ||
| 60 | IrInstruction *target_value; | |
| 61 | IrBasicBlock *else_block; | |
| 62 | size_t case_count; | |
| 63 | IrInstructionSwitchBrCase *cases; | |
| 64 | }; | |
| 65 | ||
| 66 | struct IrInstructionPhi { | |
| 67 | IrInstruction base; | |
| 68 | ||
| 69 | size_t incoming_block_count; | |
| 70 | IrBasicBlock **incoming_blocks; | |
| 71 | IrInstruction **incoming_values; | |
| 72 | }; | |
| 73 | ||
| 74 | enum IrBinOp { | |
| 75 | IrBinOpInvalid, | |
| 76 | IrBinOpBoolOr, | |
| 77 | IrBinOpBoolAnd, | |
| 78 | IrBinOpCmpEq, | |
| 79 | IrBinOpCmpNotEq, | |
| 80 | IrBinOpCmpLessThan, | |
| 81 | IrBinOpCmpGreaterThan, | |
| 82 | IrBinOpCmpLessOrEq, | |
| 83 | IrBinOpCmpGreaterOrEq, | |
| 84 | IrBinOpBinOr, | |
| 85 | IrBinOpBinXor, | |
| 86 | IrBinOpBinAnd, | |
| 87 | IrBinOpBitShiftLeft, | |
| 88 | IrBinOpBitShiftLeftWrap, | |
| 89 | IrBinOpBitShiftRight, | |
| 90 | IrBinOpAdd, | |
| 91 | IrBinOpAddWrap, | |
| 92 | IrBinOpSub, | |
| 93 | IrBinOpSubWrap, | |
| 94 | IrBinOpMult, | |
| 95 | IrBinOpMultWrap, | |
| 96 | IrBinOpDiv, | |
| 97 | IrBinOpMod, | |
| 98 | IrBinOpArrayCat, | |
| 99 | IrBinOpArrayMult, | |
| 100 | }; | |
| 101 | ||
| 102 | struct IrInstructionBinOp { | |
| 103 | IrInstruction base; | |
| 104 | ||
| 105 | IrInstruction *op1; | |
| 106 | IrBinOp op_id; | |
| 107 | IrInstruction *op2; | |
| 108 | }; | |
| 109 | ||
| 110 | struct IrInstructionLoadVar { | |
| 111 | IrInstruction base; | |
| 112 | ||
| 113 | VariableTableEntry *var; | |
| 114 | }; | |
| 115 | ||
| 116 | struct IrInstructionStoreVar { | |
| 117 | IrInstruction base; | |
| 118 | ||
| 119 | IrInstruction *value; | |
| 120 | VariableTableEntry *var; | |
| 121 | }; | |
| 122 | ||
| 123 | struct IrInstructionCall { | |
| 124 | IrInstruction base; | |
| 125 | ||
| 126 | IrInstruction *fn; | |
| 127 | size_t arg_count; | |
| 128 | IrInstruction **args; | |
| 129 | }; | |
| 130 | ||
| 131 | struct IrInstructionBuiltinCall { | |
| 132 | IrInstruction base; | |
| 133 | ||
| 134 | BuiltinFnId fn_id; | |
| 135 | size_t arg_count; | |
| 136 | IrInstruction **args; | |
| 137 | }; | |
| 138 | ||
| 139 | struct IrInstructionConst { | |
| 140 | IrInstruction base; | |
| 141 | }; | |
| 142 | ||
| 143 | struct IrInstructionReturn { | |
| 144 | IrInstruction base; | |
| 145 | ||
| 146 | IrInstruction *value; | |
| 147 | }; | |
| 148 | ||
| 149 | IrBasicBlock *ir_gen(CodeGen *g, AstNode *fn_def_node, TypeTableEntry *return_type); | |
| 150 | TypeTableEntry *ir_analyze(CodeGen *g, AstNode *fn_def_node, IrBasicBlock *entry_basic_block, | |
| 151 | TypeTableEntry *expected_type); | |
| 152 | ||
| 153 | #endif |