*WIP*
parent
0562111b02
commit
4e2fa2d15b
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue