add labels and goto
This commit is contained in:
parent
c89f77dd8e
commit
f8ca6c70c7
@ -45,7 +45,6 @@ make
|
||||
* variable declarations and assignment expressions
|
||||
* Type checking
|
||||
* loops
|
||||
* labels and goto
|
||||
* inline assembly and syscalls
|
||||
* conditional compilation and ability to check target platform and architecture
|
||||
* main function with command line arguments
|
||||
@ -110,7 +109,9 @@ PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type
|
||||
|
||||
Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
|
||||
|
||||
Statement : NonBlockExpression token(Semicolon) | BlockExpression
|
||||
Statement : Label | NonBlockExpression token(Semicolon) | BlockExpression
|
||||
|
||||
Label: token(Symbol) token(Colon)
|
||||
|
||||
Expression : BlockExpression | NonBlockExpression
|
||||
|
||||
@ -162,7 +163,9 @@ FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma)
|
||||
|
||||
PrefixOp : token(Not) | token(Dash) | token(Tilde)
|
||||
|
||||
PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol)
|
||||
PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | Goto
|
||||
|
||||
Goto: token(Goto) token(Symbol)
|
||||
|
||||
GroupedExpression : token(LParen) Expression token(RParen)
|
||||
```
|
||||
|
@ -1,13 +1,13 @@
|
||||
" Vim syntax file
|
||||
" Language: Zig
|
||||
" Maintainer: Andrew Kelley
|
||||
" Latest Revision: 27 November 2015
|
||||
" Latest Revision: 02 December 2015
|
||||
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void
|
||||
syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void goto
|
||||
syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128
|
||||
|
||||
syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
|
||||
|
@ -6,7 +6,18 @@ extern {
|
||||
fn exit(code: i32) -> unreachable;
|
||||
}
|
||||
|
||||
fn loop(a : i32) {
|
||||
if a == 0 {
|
||||
goto done;
|
||||
}
|
||||
puts("loop");
|
||||
loop(a - 1);
|
||||
|
||||
done:
|
||||
return;
|
||||
}
|
||||
|
||||
export fn _start() -> unreachable {
|
||||
puts("Hello, world!");
|
||||
loop(3);
|
||||
exit(0);
|
||||
}
|
||||
|
@ -131,6 +131,25 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
|
||||
resolve_type(g, node->data.fn_proto.return_type);
|
||||
}
|
||||
|
||||
static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry) {
|
||||
assert(node->type == NodeTypeBlock);
|
||||
|
||||
for (int i = 0; i < node->data.block.statements.length; i += 1) {
|
||||
AstNode *label_node = node->data.block.statements.at(i);
|
||||
if (label_node->type != NodeTypeLabel)
|
||||
continue;
|
||||
|
||||
LabelTableEntry *label_entry = allocate<LabelTableEntry>(1);
|
||||
label_entry->label_node = label_node;
|
||||
Buf *name = &label_node->data.label.name;
|
||||
fn_table_entry->label_table.put(name, label_entry);
|
||||
|
||||
assert(!label_node->codegen_node);
|
||||
label_node->codegen_node = allocate<CodeGenNode>(1);
|
||||
label_node->codegen_node->data.label_entry = label_entry;
|
||||
}
|
||||
}
|
||||
|
||||
static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
||||
switch (node->type) {
|
||||
case NodeTypeExternBlock:
|
||||
@ -158,6 +177,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
fn_table_entry->calling_convention = LLVMCCallConv;
|
||||
fn_table_entry->import_entry = import;
|
||||
fn_table_entry->symbol_table.init(8);
|
||||
fn_table_entry->label_table.init(8);
|
||||
|
||||
resolve_function_proto(g, fn_proto, fn_table_entry);
|
||||
|
||||
@ -208,6 +228,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
fn_table_entry->internal_linkage = is_internal;
|
||||
fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv;
|
||||
fn_table_entry->symbol_table.init(8);
|
||||
fn_table_entry->label_table.init(8);
|
||||
|
||||
g->fn_protos.append(fn_table_entry);
|
||||
g->fn_defs.append(fn_table_entry);
|
||||
@ -222,6 +243,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
assert(!proto_node->codegen_node);
|
||||
proto_node->codegen_node = allocate<CodeGenNode>(1);
|
||||
proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry;
|
||||
|
||||
preview_function_labels(g, node->data.fn_def.body, fn_table_entry);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -290,6 +313,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
case NodeTypeCastExpr:
|
||||
case NodeTypePrefixOpExpr:
|
||||
case NodeTypeIfExpr:
|
||||
case NodeTypeLabel:
|
||||
case NodeTypeGoto:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
@ -339,6 +364,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
return_type = g->builtin_types.entry_void;
|
||||
for (int i = 0; i < node->data.block.statements.length; i += 1) {
|
||||
AstNode *child = node->data.block.statements.at(i);
|
||||
if (child->type == NodeTypeLabel)
|
||||
continue;
|
||||
if (return_type == g->builtin_types.entry_unreachable) {
|
||||
if (child->type == NodeTypeVoid) {
|
||||
// {unreachable;void;void} is allowed.
|
||||
@ -365,7 +392,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
|
||||
if (actual_return_type == g->builtin_types.entry_unreachable) {
|
||||
// "return exit(0)" should just be "exit(0)".
|
||||
add_node_error(g, node, buf_sprintf("returning is unreachable."));
|
||||
add_node_error(g, node, buf_sprintf("returning is unreachable"));
|
||||
actual_return_type = g->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
@ -373,7 +400,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
return_type = g->builtin_types.entry_unreachable;
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeTypeVariableDeclaration:
|
||||
{
|
||||
zig_panic("TODO: analyze variable declaration");
|
||||
@ -382,6 +408,21 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeTypeGoto:
|
||||
{
|
||||
FnTableEntry *fn_table_entry = get_context_fn_entry(context);
|
||||
auto table_entry = fn_table_entry->label_table.maybe_get(&node->data.go_to.name);
|
||||
if (table_entry) {
|
||||
assert(!node->codegen_node);
|
||||
node->codegen_node = allocate<CodeGenNode>(1);
|
||||
node->codegen_node->data.label_entry = table_entry->value;
|
||||
} else {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("use of undeclared label '%s'", buf_ptr(&node->data.go_to.name)));
|
||||
}
|
||||
return_type = g->builtin_types.entry_unreachable;
|
||||
break;
|
||||
}
|
||||
case NodeTypeBinOpExpr:
|
||||
{
|
||||
switch (node->data.bin_op_expr.bin_op) {
|
||||
@ -563,8 +604,18 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type,
|
||||
node->data.if_expr.then_block);
|
||||
|
||||
check_type_compatibility(g, node, expected_type, else_type);
|
||||
return_type = then_type;
|
||||
TypeTableEntry *primary_type;
|
||||
TypeTableEntry *other_type;
|
||||
if (then_type == g->builtin_types.entry_unreachable) {
|
||||
primary_type = else_type;
|
||||
other_type = then_type;
|
||||
} else {
|
||||
primary_type = then_type;
|
||||
other_type = else_type;
|
||||
}
|
||||
|
||||
check_type_compatibility(g, node, expected_type, other_type);
|
||||
return_type = primary_type;
|
||||
break;
|
||||
}
|
||||
case NodeTypeDirective:
|
||||
@ -577,14 +628,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
case NodeTypeExternBlock:
|
||||
case NodeTypeFnDef:
|
||||
case NodeTypeUse:
|
||||
case NodeTypeLabel:
|
||||
zig_unreachable();
|
||||
}
|
||||
assert(return_type);
|
||||
check_type_compatibility(g, node, expected_type, return_type);
|
||||
|
||||
assert(!node->codegen_node);
|
||||
node->codegen_node = allocate<CodeGenNode>(1);
|
||||
node->codegen_node->data.expr_node.type_entry = return_type;
|
||||
if (node->codegen_node) {
|
||||
assert(node->type == NodeTypeGoto);
|
||||
} else {
|
||||
assert(node->type != NodeTypeGoto);
|
||||
node->codegen_node = allocate<CodeGenNode>(1);
|
||||
}
|
||||
node->codegen_node->expr_node.type_entry = return_type;
|
||||
|
||||
return return_type;
|
||||
}
|
||||
@ -652,6 +708,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
|
||||
case NodeTypeCastExpr:
|
||||
case NodeTypePrefixOpExpr:
|
||||
case NodeTypeIfExpr:
|
||||
case NodeTypeLabel:
|
||||
case NodeTypeGoto:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ static LLVMValueRef get_variable_value(CodeGen *g, Buf *name) {
|
||||
}
|
||||
|
||||
static TypeTableEntry *get_expr_type(AstNode *node) {
|
||||
return node->codegen_node->data.expr_node.type_entry;
|
||||
return node->codegen_node->expr_node.type_entry;
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
|
||||
@ -407,11 +407,13 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, then_block);
|
||||
LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block);
|
||||
LLVMBuildBr(g->builder, endif_block);
|
||||
if (get_expr_type(node->data.if_expr.then_block) != g->builtin_types.entry_unreachable)
|
||||
LLVMBuildBr(g->builder, endif_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, else_block);
|
||||
LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node);
|
||||
LLVMBuildBr(g->builder, endif_block);
|
||||
if (get_expr_type(node->data.if_expr.else_node) != g->builtin_types.entry_unreachable)
|
||||
LLVMBuildBr(g->builder, endif_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, endif_block);
|
||||
if (use_expr_value) {
|
||||
@ -435,7 +437,8 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, then_block);
|
||||
gen_expr(g, node->data.if_expr.then_block);
|
||||
LLVMBuildBr(g->builder, endif_block);
|
||||
if (get_expr_type(node->data.if_expr.then_block) != g->builtin_types.entry_unreachable)
|
||||
LLVMBuildBr(g->builder, endif_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, endif_block);
|
||||
return nullptr;
|
||||
@ -518,6 +521,17 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||
}
|
||||
case NodeTypeBlock:
|
||||
return gen_block(g, node, nullptr);
|
||||
case NodeTypeGoto:
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block);
|
||||
case NodeTypeLabel:
|
||||
{
|
||||
LLVMBasicBlockRef basic_block = node->codegen_node->data.label_entry->basic_block;
|
||||
add_debug_source_node(g, node);
|
||||
LLVMValueRef result = LLVMBuildBr(g->builder, basic_block);
|
||||
LLVMPositionBuilderAtEnd(g->builder, basic_block);
|
||||
return result;
|
||||
}
|
||||
case NodeTypeRoot:
|
||||
case NodeTypeRootExportDecl:
|
||||
case NodeTypeFnProto:
|
||||
@ -533,6 +547,20 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static void build_label_blocks(CodeGen *g, AstNode *block_node) {
|
||||
assert(block_node->type == NodeTypeBlock);
|
||||
for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
|
||||
AstNode *label_node = block_node->data.block.statements.at(i);
|
||||
if (label_node->type != NodeTypeLabel)
|
||||
continue;
|
||||
|
||||
Buf *name = &label_node->data.label.name;
|
||||
label_node->codegen_node->data.label_entry->basic_block = LLVMAppendBasicBlock(
|
||||
g->cur_fn->fn_value, buf_ptr(name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnProto *fn_proto,
|
||||
LLVMZigDIFile *di_file)
|
||||
{
|
||||
@ -623,10 +651,13 @@ static void do_code_gen(CodeGen *g) {
|
||||
codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn));
|
||||
LLVMGetParams(fn, codegen_fn_def->params);
|
||||
|
||||
build_label_blocks(g, fn_def_node->data.fn_def.body);
|
||||
|
||||
TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;
|
||||
gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type);
|
||||
|
||||
g->block_scopes.pop();
|
||||
|
||||
}
|
||||
assert(!g->errors.length);
|
||||
|
||||
|
13
src/main.cpp
13
src/main.cpp
@ -9,6 +9,7 @@
|
||||
#include "buffer.hpp"
|
||||
#include "codegen.hpp"
|
||||
#include "os.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@ -48,6 +49,8 @@ struct Build {
|
||||
};
|
||||
|
||||
static int build(const char *arg0, Build *b) {
|
||||
int err;
|
||||
|
||||
if (!b->in_file)
|
||||
return usage(arg0);
|
||||
|
||||
@ -59,11 +62,17 @@ static int build(const char *arg0, Build *b) {
|
||||
Buf root_source_name = BUF_INIT;
|
||||
if (buf_eql_str(&in_file_buf, "-")) {
|
||||
os_get_cwd(&root_source_dir);
|
||||
os_fetch_file(stdin, &root_source_code);
|
||||
if ((err = os_fetch_file(stdin, &root_source_code))) {
|
||||
fprintf(stderr, "unable to read stdin: %s\n", err_str(err));
|
||||
return 1;
|
||||
}
|
||||
buf_init_from_str(&root_source_name, "");
|
||||
} else {
|
||||
os_path_split(&in_file_buf, &root_source_dir, &root_source_name);
|
||||
os_fetch_file_path(buf_create_from_str(b->in_file), &root_source_code);
|
||||
if ((err = os_fetch_file_path(buf_create_from_str(b->in_file), &root_source_code))) {
|
||||
fprintf(stderr, "unable to open '%s': %s\n", b->in_file, err_str(err));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
CodeGen *g = codegen_create(&root_source_dir);
|
||||
|
@ -95,6 +95,10 @@ const char *node_type_str(NodeType node_type) {
|
||||
return "Void";
|
||||
case NodeTypeIfExpr:
|
||||
return "IfExpr";
|
||||
case NodeTypeLabel:
|
||||
return "Label";
|
||||
case NodeTypeGoto:
|
||||
return "Label";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -260,6 +264,12 @@ void ast_print(AstNode *node, int indent) {
|
||||
if (node->data.if_expr.else_node)
|
||||
ast_print(node->data.if_expr.else_node, indent + 2);
|
||||
break;
|
||||
case NodeTypeLabel:
|
||||
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.label.name));
|
||||
break;
|
||||
case NodeTypeGoto:
|
||||
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.go_to.name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -581,7 +591,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool
|
||||
}
|
||||
|
||||
/*
|
||||
PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol)
|
||||
PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | Goto
|
||||
*/
|
||||
static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
@ -609,6 +619,16 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
|
||||
ast_buf_from_token(pc, token, &node->data.symbol);
|
||||
*token_index += 1;
|
||||
return node;
|
||||
} else if (token->id == TokenIdKeywordGoto) {
|
||||
AstNode *node = ast_create_node(pc, NodeTypeGoto, token);
|
||||
*token_index += 1;
|
||||
|
||||
Token *dest_symbol = &pc->tokens->at(*token_index);
|
||||
*token_index += 1;
|
||||
ast_expect_token(pc, dest_symbol, TokenIdSymbol);
|
||||
|
||||
ast_buf_from_token(pc, dest_symbol, &node->data.go_to.name);
|
||||
return node;
|
||||
}
|
||||
|
||||
AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
|
||||
@ -1181,7 +1201,36 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma
|
||||
}
|
||||
|
||||
/*
|
||||
Statement : NonBlockExpression token(Semicolon) | BlockExpression
|
||||
Label: token(Symbol) token(Colon)
|
||||
*/
|
||||
static AstNode *ast_parse_label(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *symbol_token = &pc->tokens->at(*token_index);
|
||||
if (symbol_token->id != TokenIdSymbol) {
|
||||
if (mandatory) {
|
||||
ast_invalid_token_error(pc, symbol_token);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Token *colon_token = &pc->tokens->at(*token_index + 1);
|
||||
if (colon_token->id != TokenIdColon) {
|
||||
if (mandatory) {
|
||||
ast_invalid_token_error(pc, colon_token);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
*token_index += 2;
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeLabel, symbol_token);
|
||||
ast_buf_from_token(pc, symbol_token, &node->data.label.name);
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
Statement : Label | NonBlockExpression token(Semicolon) | BlockExpression
|
||||
Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
|
||||
*/
|
||||
static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
@ -1204,12 +1253,18 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato
|
||||
// {2;} -> {2;void}
|
||||
// {;2} -> {void;2}
|
||||
for (;;) {
|
||||
AstNode *statement_node = ast_parse_block_expr(pc, token_index, false);
|
||||
bool semicolon_expected = !statement_node;
|
||||
if (!statement_node) {
|
||||
statement_node = ast_parse_non_block_expr(pc, token_index, false);
|
||||
AstNode *statement_node = ast_parse_label(pc, token_index, false);
|
||||
bool semicolon_expected;
|
||||
if (statement_node) {
|
||||
semicolon_expected = false;
|
||||
} else {
|
||||
statement_node = ast_parse_block_expr(pc, token_index, false);
|
||||
semicolon_expected = !statement_node;
|
||||
if (!statement_node) {
|
||||
statement_node = ast_create_node(pc, NodeTypeVoid, last_token);
|
||||
statement_node = ast_parse_non_block_expr(pc, token_index, false);
|
||||
if (!statement_node) {
|
||||
statement_node = ast_create_node(pc, NodeTypeVoid, last_token);
|
||||
}
|
||||
}
|
||||
}
|
||||
node->data.block.statements.append(statement_node);
|
||||
|
@ -41,6 +41,8 @@ enum NodeType {
|
||||
NodeTypeUse,
|
||||
NodeTypeVoid,
|
||||
NodeTypeIfExpr,
|
||||
NodeTypeLabel,
|
||||
NodeTypeGoto,
|
||||
};
|
||||
|
||||
struct AstNodeRoot {
|
||||
@ -182,6 +184,14 @@ struct AstNodeIfExpr {
|
||||
AstNode *else_node; // null, block node, or other if expr node
|
||||
};
|
||||
|
||||
struct AstNodeLabel {
|
||||
Buf name;
|
||||
};
|
||||
|
||||
struct AstNodeGoto {
|
||||
Buf name;
|
||||
};
|
||||
|
||||
struct AstNode {
|
||||
enum NodeType type;
|
||||
int line;
|
||||
@ -207,6 +217,8 @@ struct AstNode {
|
||||
AstNodeFnCallExpr fn_call_expr;
|
||||
AstNodeUse use;
|
||||
AstNodeIfExpr if_expr;
|
||||
AstNodeLabel label;
|
||||
AstNodeGoto go_to;
|
||||
Buf number;
|
||||
Buf string;
|
||||
Buf symbol;
|
||||
|
@ -43,6 +43,11 @@ struct SymbolTableEntry {
|
||||
int param_index; // only valid in the case of parameters
|
||||
};
|
||||
|
||||
struct LabelTableEntry {
|
||||
AstNode *label_node;
|
||||
LLVMBasicBlockRef basic_block;
|
||||
};
|
||||
|
||||
struct FnTableEntry {
|
||||
LLVMValueRef fn_value;
|
||||
AstNode *proto_node;
|
||||
@ -54,6 +59,7 @@ struct FnTableEntry {
|
||||
|
||||
// reminder: hash tables must be initialized before use
|
||||
HashMap<Buf *, SymbolTableEntry *, buf_hash, buf_eql_buf> symbol_table;
|
||||
HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
|
||||
};
|
||||
|
||||
struct CodeGen {
|
||||
@ -100,6 +106,7 @@ struct CodeGen {
|
||||
|
||||
OutType out_type;
|
||||
FnTableEntry *cur_fn;
|
||||
LLVMBasicBlockRef cur_basic_block;
|
||||
bool c_stdint_used;
|
||||
AstNode *root_export_decl;
|
||||
int version_major;
|
||||
@ -132,9 +139,10 @@ struct CodeGenNode {
|
||||
union {
|
||||
TypeNode type_node; // for NodeTypeType
|
||||
FnDefNode fn_def_node; // for NodeTypeFnDef
|
||||
ExprNode expr_node; // for all the expression nodes
|
||||
FnProtoNode fn_proto_node; // for NodeTypeFnProto
|
||||
LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel
|
||||
} data;
|
||||
ExprNode expr_node; // for all the expression nodes
|
||||
};
|
||||
|
||||
static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) {
|
||||
|
@ -189,6 +189,8 @@ static void end_token(Tokenize *t) {
|
||||
t->cur_tok->id = TokenIdKeywordIf;
|
||||
} else if (mem_eql_str(token_mem, token_len, "else")) {
|
||||
t->cur_tok->id = TokenIdKeywordElse;
|
||||
} else if (mem_eql_str(token_mem, token_len, "goto")) {
|
||||
t->cur_tok->id = TokenIdKeywordGoto;
|
||||
}
|
||||
|
||||
t->cur_tok = nullptr;
|
||||
@ -586,6 +588,7 @@ static const char * token_name(Token *token) {
|
||||
case TokenIdKeywordVoid: return "Void";
|
||||
case TokenIdKeywordIf: return "If";
|
||||
case TokenIdKeywordElse: return "Else";
|
||||
case TokenIdKeywordGoto: return "Goto";
|
||||
case TokenIdLParen: return "LParen";
|
||||
case TokenIdRParen: return "RParen";
|
||||
case TokenIdComma: return "Comma";
|
||||
|
@ -27,6 +27,7 @@ enum TokenId {
|
||||
TokenIdKeywordVoid,
|
||||
TokenIdKeywordIf,
|
||||
TokenIdKeywordElse,
|
||||
TokenIdKeywordGoto,
|
||||
TokenIdLParen,
|
||||
TokenIdRParen,
|
||||
TokenIdComma,
|
||||
|
@ -255,6 +255,19 @@ void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder) {
|
||||
reinterpret_cast<DIBuilder*>(dibuilder)->finalize();
|
||||
}
|
||||
|
||||
LLVMZigInsertionPoint *LLVMZigSaveInsertPoint(LLVMBuilderRef builder_wrapped) {
|
||||
IRBuilderBase::InsertPoint *ip = new IRBuilderBase::InsertPoint();
|
||||
*ip = unwrap(builder_wrapped)->saveIP();
|
||||
return reinterpret_cast<LLVMZigInsertionPoint*>(ip);
|
||||
}
|
||||
|
||||
void LLVMZigRestoreInsertPoint(LLVMBuilderRef builder, LLVMZigInsertionPoint *ip_wrapped) {
|
||||
IRBuilderBase::InsertPoint *ip = reinterpret_cast<IRBuilderBase::InsertPoint*>(ip_wrapped);
|
||||
unwrap(builder)->restoreIP(*ip);
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
|
||||
enum FloatAbi {
|
||||
FloatAbiHard,
|
||||
FloatAbiSoft,
|
||||
|
@ -22,6 +22,7 @@ struct LLVMZigDIFile;
|
||||
struct LLVMZigDILexicalBlock;
|
||||
struct LLVMZigDISubprogram;
|
||||
struct LLVMZigDISubroutineType;
|
||||
struct LLVMZigInsertionPoint;
|
||||
|
||||
void LLVMZigInitializeLoopStrengthReducePass(LLVMPassRegistryRef R);
|
||||
void LLVMZigInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R);
|
||||
@ -75,6 +76,9 @@ LLVMZigDISubprogram *LLVMZigCreateFunction(LLVMZigDIBuilder *dibuilder, LLVMZigD
|
||||
|
||||
void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder);
|
||||
|
||||
LLVMZigInsertionPoint *LLVMZigSaveInsertPoint(LLVMBuilderRef builder);
|
||||
void LLVMZigRestoreInsertPoint(LLVMBuilderRef builder, LLVMZigInsertionPoint *point);
|
||||
|
||||
|
||||
/*
|
||||
* This stuff is not LLVM API but it depends on the LLVM C++ API so we put it here.
|
||||
|
@ -232,6 +232,30 @@ static void add_compiling_test_cases(void) {
|
||||
exit(0);
|
||||
}
|
||||
)SOURCE", "pass\n");
|
||||
|
||||
add_simple_case("goto", R"SOURCE(
|
||||
#link("c")
|
||||
extern {
|
||||
fn puts(s: *const u8) -> i32;
|
||||
fn exit(code: i32) -> unreachable;
|
||||
}
|
||||
|
||||
fn loop(a : i32) {
|
||||
if a == 0 {
|
||||
goto done;
|
||||
}
|
||||
puts("loop");
|
||||
loop(a - 1);
|
||||
|
||||
done:
|
||||
return;
|
||||
}
|
||||
|
||||
export fn _start() -> unreachable {
|
||||
loop(3);
|
||||
exit(0);
|
||||
}
|
||||
)SOURCE", "loop\nloop\nloop\n");
|
||||
}
|
||||
|
||||
static void add_compile_failure_test_cases(void) {
|
||||
@ -305,6 +329,17 @@ fn a() {
|
||||
)SOURCE", 2,
|
||||
".tmp_source.zig:3:5: error: use of undeclared identifier 'b'",
|
||||
".tmp_source.zig:4:5: error: use of undeclared identifier 'c'");
|
||||
|
||||
add_compile_fail_case("goto cause unreachable code", R"SOURCE(
|
||||
fn a() {
|
||||
goto done;
|
||||
b();
|
||||
done:
|
||||
return;
|
||||
}
|
||||
fn b() {}
|
||||
)SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code");
|
||||
|
||||
}
|
||||
|
||||
static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user