master
Andrew Kelley 2016-09-30 02:21:21 -04:00
parent 0562111b02
commit 4e2fa2d15b
4 changed files with 404 additions and 48 deletions

View File

@ -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/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/c_tokenizer.cpp"
"${CMAKE_SOURCE_DIR}/src/codegen.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/util.cpp"
"${CMAKE_SOURCE_DIR}/src/errmsg.cpp"
"${CMAKE_SOURCE_DIR}/src/zig_llvm.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/zig_llvm.cpp"
)
set(TEST_SOURCES

View File

@ -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,25 +7098,16 @@ 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);
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);
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);
}
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)));
}
}
fn_table_entry->anal_state = FnAnalStateComplete;
}

226
src/ir.cpp Normal file
View File

@ -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);
}
}

153
src/ir.hpp Normal file
View File

@ -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