diff options
| author | Josh Wolfe <thejoshwolfe@gmail.com> | 2015-11-30 22:12:21 -0700 |
|---|---|---|
| committer | Josh Wolfe <thejoshwolfe@gmail.com> | 2015-11-30 22:12:21 -0700 |
| commit | 00f4c05784c05552e1e379b98c09161c936cfb31 (patch) | |
| tree | 5b2234cae585c5ac3a2cb23503908b0a6e2a27e3 /src | |
| parent | abbc3957019c3a12dacd54869ff18b91c3f07699 (diff) | |
| parent | 55b8472374eede496b59396dbe253b05b16063e1 (diff) | |
| download | zig-00f4c05784c05552e1e379b98c09161c936cfb31.tar.gz zig-00f4c05784c05552e1e379b98c09161c936cfb31.zip | |
merge conflicts
Diffstat (limited to 'src')
| -rw-r--r-- | src/analyze.cpp | 177 | ||||
| -rw-r--r-- | src/analyze.hpp | 3 | ||||
| -rw-r--r-- | src/codegen.cpp | 258 | ||||
| -rw-r--r-- | src/codegen.hpp | 11 | ||||
| -rw-r--r-- | src/error.cpp | 1 | ||||
| -rw-r--r-- | src/error.hpp | 1 | ||||
| -rw-r--r-- | src/main.cpp | 170 | ||||
| -rw-r--r-- | src/os.cpp | 55 | ||||
| -rw-r--r-- | src/os.hpp | 9 | ||||
| -rw-r--r-- | src/parser.cpp | 42 | ||||
| -rw-r--r-- | src/parser.hpp | 7 | ||||
| -rw-r--r-- | src/semantic_info.hpp | 37 | ||||
| -rw-r--r-- | src/tokenizer.cpp | 3 | ||||
| -rw-r--r-- | src/tokenizer.hpp | 1 |
14 files changed, 494 insertions, 281 deletions
diff --git a/src/analyze.cpp b/src/analyze.cpp index 7e02b1eb89..2d88389dab 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -119,7 +119,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node) { resolve_type(g, node->data.fn_proto.return_type); } -static void preview_function_declarations(CodeGen *g, AstNode *node) { +static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *node) { switch (node->type) { case NodeTypeExternBlock: for (int i = 0; i < node->data.extern_block.directives->length; i += 1) { @@ -145,6 +145,7 @@ static void preview_function_declarations(CodeGen *g, AstNode *node) { fn_table_entry->proto_node = fn_proto; fn_table_entry->is_extern = true; fn_table_entry->calling_convention = LLVMCCallConv; + fn_table_entry->import_entry = import; g->fn_table.put(name, fn_table_entry); } break; @@ -162,6 +163,7 @@ static void preview_function_declarations(CodeGen *g, AstNode *node) { node->codegen_node->data.fn_def_node.skip = true; } else { FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1); + fn_table_entry->import_entry = import; fn_table_entry->proto_node = proto_node; fn_table_entry->fn_def_node = node; fn_table_entry->internal_linkage = proto_node->data.fn_proto.visib_mod != FnProtoVisibModExport; @@ -196,8 +198,8 @@ static void preview_function_declarations(CodeGen *g, AstNode *node) { } else { g->root_export_decl = node; - if (!g->out_name) - g->out_name = &node->data.root_export_decl.name; + if (!g->root_out_name) + g->root_out_name = &node->data.root_export_decl.name; Buf *out_type = &node->data.root_export_decl.type; OutType export_out_type; @@ -215,6 +217,9 @@ static void preview_function_declarations(CodeGen *g, AstNode *node) { g->out_type = export_out_type; } break; + case NodeTypeUse: + zig_panic("TODO use"); + break; case NodeTypeDirective: case NodeTypeParamDecl: case NodeTypeFnProto: @@ -379,6 +384,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, BlockContext *context, Ty case NodeTypeRootExportDecl: case NodeTypeExternBlock: case NodeTypeFnDef: + case NodeTypeUse: zig_unreachable(); } zig_unreachable(); @@ -437,6 +443,75 @@ static void check_fn_def_control_flow(CodeGen *g, AstNode *node) { } } +static void analyze_expression(CodeGen *g, AstNode *node) { + switch (node->type) { + case NodeTypeBlock: + for (int i = 0; i < node->data.block.statements.length; i += 1) { + AstNode *child = node->data.block.statements.at(i); + analyze_expression(g, child); + } + break; + case NodeTypeReturnExpr: + if (node->data.return_expr.expr) { + analyze_expression(g, node->data.return_expr.expr); + } + break; + case NodeTypeBinOpExpr: + analyze_expression(g, node->data.bin_op_expr.op1); + analyze_expression(g, node->data.bin_op_expr.op2); + break; + case NodeTypeFnCallExpr: + { + Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr); + + auto entry = g->fn_table.maybe_get(name); + if (!entry) { + add_node_error(g, node, + buf_sprintf("undefined function: '%s'", buf_ptr(name))); + } else { + FnTableEntry *fn_table_entry = entry->value; + assert(fn_table_entry->proto_node->type == NodeTypeFnProto); + int expected_param_count = fn_table_entry->proto_node->data.fn_proto.params.length; + int actual_param_count = node->data.fn_call_expr.params.length; + if (expected_param_count != actual_param_count) { + add_node_error(g, node, + buf_sprintf("wrong number of arguments. Expected %d, got %d.", + expected_param_count, actual_param_count)); + } + } + + for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) { + AstNode *child = node->data.fn_call_expr.params.at(i); + analyze_expression(g, child); + } + break; + } + case NodeTypeCastExpr: + zig_panic("TODO"); + break; + case NodeTypePrefixOpExpr: + zig_panic("TODO"); + break; + case NodeTypeNumberLiteral: + case NodeTypeStringLiteral: + case NodeTypeUnreachable: + case NodeTypeSymbol: + // nothing to do + break; + case NodeTypeDirective: + case NodeTypeFnDecl: + case NodeTypeFnProto: + case NodeTypeParamDecl: + case NodeTypeType: + case NodeTypeRoot: + case NodeTypeRootExportDecl: + case NodeTypeExternBlock: + case NodeTypeFnDef: + case NodeTypeUse: + zig_unreachable(); + } +} + static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { switch (node->type) { case NodeTypeFnDef: @@ -470,9 +545,9 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { case NodeTypeRootExportDecl: case NodeTypeExternBlock: + case NodeTypeUse: // already looked at these in the preview pass break; - case NodeTypeDirective: case NodeTypeParamDecl: case NodeTypeFnProto: @@ -493,13 +568,13 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { } } -static void analyze_root(CodeGen *g, AstNode *node) { +static void analyze_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { assert(node->type == NodeTypeRoot); // find function declarations for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { AstNode *child = node->data.root.top_level_decls.at(i); - preview_function_declarations(g, child); + preview_function_declarations(g, import, child); } for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { @@ -507,7 +582,7 @@ static void analyze_root(CodeGen *g, AstNode *node) { analyze_top_level_declaration(g, child); } - if (!g->out_name) { + if (!g->root_out_name) { add_node_error(g, node, buf_sprintf("missing export declaration and output name not provided")); } else if (g->out_type == OutTypeUnknown) { @@ -516,91 +591,7 @@ static void analyze_root(CodeGen *g, AstNode *node) { } } -static void define_primitive_types(CodeGen *g) { - { - // if this type is anywhere in the AST, we should never hit codegen. - TypeTableEntry *entry = allocate<TypeTableEntry>(1); - buf_init_from_str(&entry->name, "(invalid)"); - g->builtin_types.entry_invalid = entry; - } - { - TypeTableEntry *entry = allocate<TypeTableEntry>(1); - entry->type_ref = LLVMInt8Type(); - buf_init_from_str(&entry->name, "u8"); - entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 8, 8, - LLVMZigEncoding_DW_ATE_unsigned()); - g->type_table.put(&entry->name, entry); - g->builtin_types.entry_u8 = entry; - } - { - TypeTableEntry *entry = allocate<TypeTableEntry>(1); - entry->type_ref = LLVMInt32Type(); - buf_init_from_str(&entry->name, "i32"); - entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 32, 32, - LLVMZigEncoding_DW_ATE_signed()); - g->type_table.put(&entry->name, entry); - g->builtin_types.entry_i32 = entry; - } - { - TypeTableEntry *entry = allocate<TypeTableEntry>(1); - entry->type_ref = LLVMVoidType(); - buf_init_from_str(&entry->name, "void"); - entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 0, 0, - LLVMZigEncoding_DW_ATE_unsigned()); - g->type_table.put(&entry->name, entry); - g->builtin_types.entry_void = entry; - } - { - TypeTableEntry *entry = allocate<TypeTableEntry>(1); - entry->type_ref = LLVMVoidType(); - buf_init_from_str(&entry->name, "unreachable"); - entry->di_type = g->builtin_types.entry_void->di_type; - g->type_table.put(&entry->name, entry); - g->builtin_types.entry_unreachable = entry; - } -} - - -void semantic_analyze(CodeGen *g) { - LLVMInitializeAllTargets(); - LLVMInitializeAllTargetMCs(); - LLVMInitializeAllAsmPrinters(); - LLVMInitializeAllAsmParsers(); - LLVMInitializeNativeTarget(); - - g->is_native_target = true; - char *native_triple = LLVMGetDefaultTargetTriple(); - - LLVMTargetRef target_ref; - char *err_msg = nullptr; - if (LLVMGetTargetFromTriple(native_triple, &target_ref, &err_msg)) { - zig_panic("unable to get target from triple: %s", err_msg); - } - - char *native_cpu = LLVMZigGetHostCPUName(); - char *native_features = LLVMZigGetNativeFeatures(); - - LLVMCodeGenOptLevel opt_level = (g->build_type == CodeGenBuildTypeDebug) ? - LLVMCodeGenLevelNone : LLVMCodeGenLevelAggressive; - - LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC; - - g->target_machine = LLVMCreateTargetMachine(target_ref, native_triple, - native_cpu, native_features, opt_level, reloc_mode, LLVMCodeModelDefault); - - g->target_data_ref = LLVMGetTargetMachineData(g->target_machine); - - - g->module = LLVMModuleCreateWithName("ZigModule"); - - g->pointer_size_bytes = LLVMPointerSize(g->target_data_ref); - - g->builder = LLVMCreateBuilder(); - g->dbuilder = LLVMZigCreateDIBuilder(g->module, true); - - - define_primitive_types(g); - - analyze_root(g, g->root); +void semantic_analyze(CodeGen *g, ImportTableEntry *import_table_entry) { + analyze_root(g, import_table_entry, import_table_entry->root); } diff --git a/src/analyze.hpp b/src/analyze.hpp index 0dca23194d..374fcad748 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -9,7 +9,8 @@ #define ZIG_ANALYZE_HPP struct CodeGen; +struct ImportTableEntry; -void semantic_analyze(CodeGen *g); +void semantic_analyze(CodeGen *g, ImportTableEntry *entry); #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index c5e90cf75a..cb1b1b222e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -11,26 +11,21 @@ #include "os.hpp" #include "config.h" #include "error.hpp" - #include "semantic_info.hpp" +#include "analyze.hpp" #include <stdio.h> #include <errno.h> -CodeGen *create_codegen(AstNode *root, Buf *in_full_path) { +CodeGen *codegen_create(Buf *root_source_dir) { CodeGen *g = allocate<CodeGen>(1); - g->root = root; g->fn_table.init(32); g->str_table.init(32); g->type_table.init(32); g->link_table.init(32); - g->is_static = false; + g->import_table.init(32); g->build_type = CodeGenBuildTypeDebug; - g->strip_debug_symbols = false; - g->out_name = nullptr; - g->out_type = OutTypeUnknown; - - os_path_split(in_full_path, &g->in_dir, &g->in_file); + g->root_source_dir = root_source_dir; return g; } @@ -42,6 +37,10 @@ void codegen_set_is_static(CodeGen *g, bool is_static) { g->is_static = is_static; } +void codegen_set_verbose(CodeGen *g, bool verbose) { + g->verbose = verbose; +} + void codegen_set_strip(CodeGen *g, bool strip) { g->strip_debug_symbols = strip; } @@ -51,7 +50,7 @@ void codegen_set_out_type(CodeGen *g, OutType out_type) { } void codegen_set_out_name(CodeGen *g, Buf *out_name) { - g->out_name = out_name; + g->root_out_name = out_name; } static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node); @@ -425,16 +424,17 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeBlock: case NodeTypeExternBlock: case NodeTypeDirective: + case NodeTypeUse: zig_unreachable(); } zig_unreachable(); } -static void gen_block(CodeGen *g, AstNode *block_node, bool add_implicit_return) { +static void gen_block(CodeGen *g, ImportTableEntry *import, AstNode *block_node, bool add_implicit_return) { assert(block_node->type == NodeTypeBlock); LLVMZigDILexicalBlock *di_block = LLVMZigCreateLexicalBlock(g->dbuilder, g->block_scopes.last(), - g->di_file, block_node->line + 1, block_node->column + 1); + import->di_file, block_node->line + 1, block_node->column + 1); g->block_scopes.append(LLVMZigLexicalBlockToScope(di_block)); add_debug_source_node(g, block_node); @@ -466,22 +466,11 @@ static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnPro return LLVMZigCreateSubroutineType(g->dbuilder, di_file, types, types_len, 0); } -void code_gen(CodeGen *g) { +static void do_code_gen(CodeGen *g) { assert(!g->errors.length); - Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING); - bool is_optimized = g->build_type == CodeGenBuildTypeRelease; - const char *flags = ""; - unsigned runtime_version = 0; - g->compile_unit = LLVMZigCreateCompileUnit(g->dbuilder, LLVMZigLang_DW_LANG_C99(), - buf_ptr(&g->in_file), buf_ptr(&g->in_dir), - buf_ptr(producer), is_optimized, flags, runtime_version, - "", 0, !g->strip_debug_symbols); - g->block_scopes.append(LLVMZigCompileUnitToScope(g->compile_unit)); - g->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(&g->in_file), buf_ptr(&g->in_dir)); - // Generate function prototypes auto it = g->fn_table.entry_iterator(); @@ -523,6 +512,7 @@ void code_gen(CodeGen *g) { // Generate function definitions. for (int i = 0; i < g->fn_defs.length; i += 1) { FnTableEntry *fn_table_entry = g->fn_defs.at(i); + ImportTableEntry *import = fn_table_entry->import_entry; AstNode *fn_def_node = fn_table_entry->fn_def_node; LLVMValueRef fn = fn_table_entry->fn_value; g->cur_fn = fn_table_entry; @@ -532,14 +522,15 @@ void code_gen(CodeGen *g) { AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; // Add debug info. - LLVMZigDIScope *fn_scope = LLVMZigFileToScope(g->di_file); + LLVMZigDIScope *fn_scope = LLVMZigFileToScope(import->di_file); unsigned line_number = fn_def_node->line + 1; unsigned scope_line = line_number; bool is_definition = true; unsigned flags = 0; + bool is_optimized = g->build_type == CodeGenBuildTypeRelease; LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder, - fn_scope, buf_ptr(&fn_proto->name), "", g->di_file, line_number, - create_di_function_type(g, fn_proto, g->di_file), fn_table_entry->internal_linkage, + fn_scope, buf_ptr(&fn_proto->name), "", import->di_file, line_number, + create_di_function_type(g, fn_proto, import->di_file), fn_table_entry->internal_linkage, is_definition, scope_line, flags, is_optimized, fn); g->block_scopes.append(LLVMZigSubprogramToScope(subprogram)); @@ -555,7 +546,7 @@ void code_gen(CodeGen *g) { LLVMGetParams(fn, codegen_fn_def->params); bool add_implicit_return = codegen_fn_def->add_implicit_return; - gen_block(g, fn_def_node->data.fn_def.body, add_implicit_return); + gen_block(g, import, fn_def_node->data.fn_def.body, add_implicit_return); g->block_scopes.pop(); } @@ -563,7 +554,9 @@ void code_gen(CodeGen *g) { LLVMZigDIBuilderFinalize(g->dbuilder); - LLVMDumpModule(g->module); + if (g->verbose) { + LLVMDumpModule(g->module); + } // in release mode, we're sooooo confident that we've generated correct ir, // that we skip the verify module step in order to get better performance. @@ -573,13 +566,171 @@ void code_gen(CodeGen *g) { #endif } -void code_gen_optimize(CodeGen *g) { - LLVMZigOptimizeModule(g->target_machine, g->module); - LLVMDumpModule(g->module); +static void define_primitive_types(CodeGen *g) { + { + // if this type is anywhere in the AST, we should never hit codegen. + TypeTableEntry *entry = allocate<TypeTableEntry>(1); + buf_init_from_str(&entry->name, "(invalid)"); + g->builtin_types.entry_invalid = entry; + } + { + TypeTableEntry *entry = allocate<TypeTableEntry>(1); + entry->type_ref = LLVMInt8Type(); + buf_init_from_str(&entry->name, "u8"); + entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 8, 8, + LLVMZigEncoding_DW_ATE_unsigned()); + g->type_table.put(&entry->name, entry); + g->builtin_types.entry_u8 = entry; + } + { + TypeTableEntry *entry = allocate<TypeTableEntry>(1); + entry->type_ref = LLVMInt32Type(); + buf_init_from_str(&entry->name, "i32"); + entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 32, 32, + LLVMZigEncoding_DW_ATE_signed()); + g->type_table.put(&entry->name, entry); + g->builtin_types.entry_i32 = entry; + } + { + TypeTableEntry *entry = allocate<TypeTableEntry>(1); + entry->type_ref = LLVMVoidType(); + buf_init_from_str(&entry->name, "void"); + entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 0, 0, + LLVMZigEncoding_DW_ATE_unsigned()); + g->type_table.put(&entry->name, entry); + g->builtin_types.entry_void = entry; + } + { + TypeTableEntry *entry = allocate<TypeTableEntry>(1); + entry->type_ref = LLVMVoidType(); + buf_init_from_str(&entry->name, "unreachable"); + entry->di_type = g->builtin_types.entry_void->di_type; + g->type_table.put(&entry->name, entry); + g->builtin_types.entry_unreachable = entry; + } } -ZigList<ErrorMsg> *codegen_error_messages(CodeGen *g) { - return &g->errors; + + +static void init(CodeGen *g, Buf *source_path) { + LLVMInitializeAllTargets(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllAsmPrinters(); + LLVMInitializeAllAsmParsers(); + LLVMInitializeNativeTarget(); + + g->is_native_target = true; + char *native_triple = LLVMGetDefaultTargetTriple(); + + LLVMTargetRef target_ref; + char *err_msg = nullptr; + if (LLVMGetTargetFromTriple(native_triple, &target_ref, &err_msg)) { + zig_panic("unable to get target from triple: %s", err_msg); + } + + char *native_cpu = LLVMZigGetHostCPUName(); + char *native_features = LLVMZigGetNativeFeatures(); + + LLVMCodeGenOptLevel opt_level = (g->build_type == CodeGenBuildTypeDebug) ? + LLVMCodeGenLevelNone : LLVMCodeGenLevelAggressive; + + LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC; + + g->target_machine = LLVMCreateTargetMachine(target_ref, native_triple, + native_cpu, native_features, opt_level, reloc_mode, LLVMCodeModelDefault); + + g->target_data_ref = LLVMGetTargetMachineData(g->target_machine); + + + g->module = LLVMModuleCreateWithName("ZigModule"); + + g->pointer_size_bytes = LLVMPointerSize(g->target_data_ref); + + g->builder = LLVMCreateBuilder(); + g->dbuilder = LLVMZigCreateDIBuilder(g->module, true); + + + define_primitive_types(g); + + Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING); + bool is_optimized = g->build_type == CodeGenBuildTypeRelease; + const char *flags = ""; + unsigned runtime_version = 0; + g->compile_unit = LLVMZigCreateCompileUnit(g->dbuilder, LLVMZigLang_DW_LANG_C99(), + buf_ptr(source_path), buf_ptr(g->root_source_dir), + buf_ptr(producer), is_optimized, flags, runtime_version, + "", 0, !g->strip_debug_symbols); + + +} + +void codegen_add_code(CodeGen *g, Buf *source_path, Buf *source_code) { + if (!g->initialized) { + g->initialized = true; + init(g, source_path); + } + + Buf full_path = BUF_INIT; + os_path_join(g->root_source_dir, source_path, &full_path); + + Buf dirname = BUF_INIT; + Buf basename = BUF_INIT; + os_path_split(&full_path, &dirname, &basename); + + if (g->verbose) { + fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(source_path)); + fprintf(stderr, "----------------\n"); + fprintf(stderr, "%s\n", buf_ptr(source_code)); + + fprintf(stderr, "\nTokens:\n"); + fprintf(stderr, "---------\n"); + } + + ZigList<Token> *tokens = tokenize(source_code); + + if (g->verbose) { + print_tokens(source_code, tokens); + + fprintf(stderr, "\nAST:\n"); + fprintf(stderr, "------\n"); + } + + ImportTableEntry *import_entry = allocate<ImportTableEntry>(1); + import_entry->root = ast_parse(source_code, tokens); + assert(import_entry->root); + if (g->verbose) { + ast_print(import_entry->root, 0); + + fprintf(stderr, "\nSemantic Analysis:\n"); + fprintf(stderr, "--------------------\n"); + } + + import_entry->path = source_path; + import_entry->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(&basename), buf_ptr(&dirname)); + g->import_table.put(source_path, import_entry); + + semantic_analyze(g, import_entry); + + if (g->errors.length == 0) { + if (g->verbose) { + fprintf(stderr, "OK\n"); + } + } else { + for (int i = 0; i < g->errors.length; i += 1) { + ErrorMsg *err = &g->errors.at(i); + fprintf(stderr, "Error: Line %d, column %d: %s\n", + err->line_start + 1, err->column_start + 1, + buf_ptr(err->msg)); + } + exit(1); + } + + if (g->verbose) { + fprintf(stderr, "\nCode Generation:\n"); + fprintf(stderr, "------------------\n"); + } + + do_code_gen(g); } static Buf *to_c_type(CodeGen *g, AstNode *type_node) { @@ -601,15 +752,15 @@ static Buf *to_c_type(CodeGen *g, AstNode *type_node) { } static void generate_h_file(CodeGen *g) { - Buf *h_file_out_path = buf_sprintf("%s.h", buf_ptr(g->out_name)); + Buf *h_file_out_path = buf_sprintf("%s.h", buf_ptr(g->root_out_name)); FILE *out_h = fopen(buf_ptr(h_file_out_path), "wb"); if (!out_h) zig_panic("unable to open %s: %s", buf_ptr(h_file_out_path), strerror(errno)); - Buf *export_macro = buf_sprintf("%s_EXPORT", buf_ptr(g->out_name)); + Buf *export_macro = buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name)); buf_upcase(export_macro); - Buf *extern_c_macro = buf_sprintf("%s_EXTERN_C", buf_ptr(g->out_name)); + Buf *extern_c_macro = buf_sprintf("%s_EXTERN_C", buf_ptr(g->root_out_name)); buf_upcase(extern_c_macro); Buf h_buf = BUF_INIT; @@ -644,7 +795,8 @@ static void generate_h_file(CodeGen *g) { } } - Buf *ifdef_dance_name = buf_sprintf("%s_%s_H", buf_ptr(g->out_name), buf_ptr(g->out_name)); + Buf *ifdef_dance_name = buf_sprintf("%s_%s_H", + buf_ptr(g->root_out_name), buf_ptr(g->root_out_name)); buf_upcase(ifdef_dance_name); fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name)); @@ -677,9 +829,27 @@ static void generate_h_file(CodeGen *g) { zig_panic("unable to close h file: %s", strerror(errno)); } -void code_gen_link(CodeGen *g, const char *out_file) { +void codegen_link(CodeGen *g, const char *out_file) { + bool is_optimized = (g->build_type == CodeGenBuildTypeRelease); + if (is_optimized) { + if (g->verbose) { + fprintf(stderr, "\nOptimization:\n"); + fprintf(stderr, "---------------\n"); + } + + LLVMZigOptimizeModule(g->target_machine, g->module); + + if (g->verbose) { + LLVMDumpModule(g->module); + } + } + if (g->verbose) { + fprintf(stderr, "\nLink:\n"); + fprintf(stderr, "-------\n"); + } + if (!out_file) { - out_file = buf_ptr(g->out_name); + out_file = buf_ptr(g->root_out_name); } Buf out_file_o = BUF_INIT; @@ -728,8 +898,8 @@ void code_gen_link(CodeGen *g, const char *out_file) { if (g->out_type == OutTypeLib) { Buf *out_lib_so = buf_sprintf("lib%s.so.%d.%d.%d", - buf_ptr(g->out_name), g->version_major, g->version_minor, g->version_patch); - Buf *soname = buf_sprintf("lib%s.so.%d", buf_ptr(g->out_name), g->version_major); + buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); + Buf *soname = buf_sprintf("lib%s.so.%d", buf_ptr(g->root_out_name), g->version_major); args.append("-shared"); args.append("-soname"); args.append(buf_ptr(soname)); @@ -756,4 +926,8 @@ void code_gen_link(CodeGen *g, const char *out_file) { if (g->out_type == OutTypeLib) { generate_h_file(g); } + + if (g->verbose) { + fprintf(stderr, "OK\n"); + } } diff --git a/src/codegen.hpp b/src/codegen.hpp index 7795a3d3d9..403931b834 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -29,7 +29,7 @@ struct ErrorMsg { }; -CodeGen *create_codegen(AstNode *root, Buf *in_file); +CodeGen *codegen_create(Buf *root_source_dir); enum CodeGenBuildType { CodeGenBuildTypeDebug, @@ -38,15 +38,12 @@ enum CodeGenBuildType { void codegen_set_build_type(CodeGen *codegen, CodeGenBuildType build_type); void codegen_set_is_static(CodeGen *codegen, bool is_static); void codegen_set_strip(CodeGen *codegen, bool strip); +void codegen_set_verbose(CodeGen *codegen, bool verbose); void codegen_set_out_type(CodeGen *codegen, OutType out_type); void codegen_set_out_name(CodeGen *codegen, Buf *out_name); -void code_gen_optimize(CodeGen *g); +void codegen_add_code(CodeGen *g, Buf *source_path, Buf *source_code); -void code_gen(CodeGen *g); - -void code_gen_link(CodeGen *g, const char *out_file); - -ZigList<ErrorMsg> *codegen_error_messages(CodeGen *g); +void codegen_link(CodeGen *g, const char *out_file); #endif diff --git a/src/error.cpp b/src/error.cpp index e7dab68147..7c3064c143 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -5,6 +5,7 @@ const char *err_str(int err) { case ErrorNone: return "(no error)"; case ErrorNoMem: return "out of memory"; case ErrorInvalidFormat: return "invalid format"; + case ErrorSemanticAnalyzeFail: return "semantic analyze failed"; } return "(invalid error)"; } diff --git a/src/error.hpp b/src/error.hpp index 3a975f2177..7da2cf8322 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -12,6 +12,7 @@ enum Error { ErrorNone, ErrorNoMem, ErrorInvalidFormat, + ErrorSemanticAnalyzeFail, }; const char *err_str(int err); diff --git a/src/main.cpp b/src/main.cpp index a39bb8feb5..b88d6ecd6b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,25 +6,11 @@ */ #include "config.h" -#include "util.hpp" -#include "list.hpp" #include "buffer.hpp" -#include "parser.hpp" -#include "tokenizer.hpp" -#include "error.hpp" #include "codegen.hpp" -#include "analyze.hpp" +#include "os.hpp" #include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <limits.h> -#include <stdint.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <inttypes.h> static int usage(const char *arg0) { fprintf(stderr, "Usage: %s [command] [options] target\n" @@ -38,6 +24,7 @@ static int usage(const char *arg0) { " --export [exe|lib|obj] override output type\n" " --name [name] override output name\n" " --output [file] override destination path\n" + " --verbose turn on compiler debug output\n" , arg0); return EXIT_FAILURE; } @@ -47,98 +34,47 @@ static int version(void) { return EXIT_SUCCESS; } -static Buf *fetch_file(FILE *f) { - int fd = fileno(f); - struct stat st; - if (fstat(fd, &st)) - zig_panic("unable to stat file: %s", strerror(errno)); - off_t big_size = st.st_size; - if (big_size > INT_MAX) - zig_panic("file too big"); - int size = (int)big_size; - - Buf *buf = buf_alloc_fixed(size); - size_t amt_read = fread(buf_ptr(buf), 1, buf_len(buf), f); - if (amt_read != (size_t)buf_len(buf)) - zig_panic("error reading: %s", strerror(errno)); - - return buf; -} - -static int build(const char *arg0, const char *in_file, const char *out_file, bool release, - bool strip, bool is_static, OutType out_type, char *out_name) -{ - static char cur_dir[1024]; +struct Build { + const char *in_file; + const char *out_file; + bool release; + bool strip; + bool is_static; + OutType out_type; + const char *out_name; + bool verbose; +}; - if (!in_file) +static int build(const char *arg0, Build *b) { + if (!b->in_file) return usage(arg0); - FILE *in_f; - if (strcmp(in_file, "-") == 0) { - in_f = stdin; - char *result = getcwd(cur_dir, sizeof(cur_dir)); - if (!result) - zig_panic("unable to get current working directory: %s", strerror(errno)); - } else { - in_f = fopen(in_file, "rb"); - if (!in_f) - zig_panic("unable to open %s for reading: %s\n", in_file, strerror(errno)); - } + Buf in_file_buf = BUF_INIT; + buf_init_from_str(&in_file_buf, b->in_file); - fprintf(stderr, "Original source:\n"); - fprintf(stderr, "----------------\n"); - Buf *in_data = fetch_file(in_f); - fprintf(stderr, "%s\n", buf_ptr(in_data)); - - fprintf(stderr, "\nTokens:\n"); - fprintf(stderr, "---------\n"); - ZigList<Token> *tokens = tokenize(in_data); - print_tokens(in_data, tokens); - - fprintf(stderr, "\nAST:\n"); - fprintf(stderr, "------\n"); - AstNode *root = ast_parse(in_data, tokens); - assert(root); - ast_print(root, 0); - - fprintf(stderr, "\nSemantic Analysis:\n"); - fprintf(stderr, "--------------------\n"); - CodeGen *codegen = create_codegen(root, buf_create_from_str(in_file)); - codegen_set_build_type(codegen, release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug); - codegen_set_strip(codegen, strip); - codegen_set_is_static(codegen, is_static); - if (out_type != OutTypeUnknown) - codegen_set_out_type(codegen, out_type); - if (out_name) - codegen_set_out_name(codegen, buf_create_from_str(out_name)); - semantic_analyze(codegen); - ZigList<ErrorMsg> *errors = codegen_error_messages(codegen); - if (errors->length == 0) { - fprintf(stderr, "OK\n"); + Buf root_source_dir = BUF_INIT; + Buf root_source_code = BUF_INIT; + 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); + buf_init_from_str(&root_source_name, ""); } else { - for (int i = 0; i < errors->length; i += 1) { - ErrorMsg *err = &errors->at(i); - fprintf(stderr, "Error: Line %d, column %d: %s\n", - err->line_start + 1, err->column_start + 1, - buf_ptr(err->msg)); - } - return 1; + 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); } - fprintf(stderr, "\nCode Generation:\n"); - fprintf(stderr, "------------------\n"); - code_gen(codegen); - - if (release) { - fprintf(stderr, "\nOptimization:\n"); - fprintf(stderr, "---------------\n"); - code_gen_optimize(codegen); - } - - fprintf(stderr, "\nLink:\n"); - fprintf(stderr, "-------\n"); - code_gen_link(codegen, out_file); - fprintf(stderr, "OK\n"); + CodeGen *g = codegen_create(&root_source_dir); + codegen_set_build_type(g, b->release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug); + codegen_set_strip(g, b->strip); + codegen_set_is_static(g, b->is_static); + if (b->out_type != OutTypeUnknown) + codegen_set_out_type(g, b->out_type); + if (b->out_name) + codegen_set_out_name(g, buf_create_from_str(b->out_name)); + codegen_set_verbose(g, b->verbose); + codegen_add_code(g, &root_source_name, &root_source_code); + codegen_link(g, b->out_file); return 0; } @@ -151,43 +87,39 @@ enum Cmd { int main(int argc, char **argv) { char *arg0 = argv[0]; - char *in_file = NULL; - char *out_file = NULL; - bool release = false; - bool strip = false; - bool is_static = false; - - OutType out_type = OutTypeUnknown; - char *out_name = NULL; + Build b = {0}; Cmd cmd = CmdNone; + for (int i = 1; i < argc; i += 1) { char *arg = argv[i]; if (arg[0] == '-' && arg[1] == '-') { if (strcmp(arg, "--release") == 0) { - release = true; + b.release = true; } else if (strcmp(arg, "--strip") == 0) { - strip = true; + b.strip = true; } else if (strcmp(arg, "--static") == 0) { - is_static = true; + b.is_static = true; + } else if (strcmp(arg, "--verbose") == 0) { + b.verbose = true; } else if (i + 1 >= argc) { return usage(arg0); } else { i += 1; if (strcmp(arg, "--output") == 0) { - out_file = argv[i]; + b.out_file = argv[i]; } else if (strcmp(arg, "--export") == 0) { if (strcmp(argv[i], "exe") == 0) { - out_type = OutTypeExe; + b.out_type = OutTypeExe; } else if (strcmp(argv[i], "lib") == 0) { - out_type = OutTypeLib; + b.out_type = OutTypeLib; } else if (strcmp(argv[i], "obj") == 0) { - out_type = OutTypeObj; + b.out_type = OutTypeObj; } else { return usage(arg0); } } else if (strcmp(arg, "--name") == 0) { - out_name = argv[i]; + b.out_name = argv[i]; } else { return usage(arg0); } @@ -206,8 +138,8 @@ int main(int argc, char **argv) { case CmdNone: zig_unreachable(); case CmdBuild: - if (!in_file) { - in_file = arg; + if (!b.in_file) { + b.in_file = arg; } else { return usage(arg0); } @@ -222,7 +154,7 @@ int main(int argc, char **argv) { case CmdNone: return usage(arg0); case CmdBuild: - return build(arg0, in_file, out_file, release, strip, is_static, out_type, out_name); + return build(arg0, &b); case CmdVersion: return version(); } diff --git a/src/os.cpp b/src/os.cpp index bab72de2b9..61dd1c01ee 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -13,8 +13,8 @@ #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> -#include <stdio.h> #include <fcntl.h> +#include <limits.h> void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached) { pid_t pid = fork(); @@ -37,7 +37,7 @@ void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detache zig_panic("execvp failed: %s", strerror(errno)); } -static void read_all_fd(int fd, Buf *out_buf) { +static void read_all_fd_stream(int fd, Buf *out_buf) { static const ssize_t buf_size = 0x2000; buf_resize(out_buf, buf_size); ssize_t actual_buf_len = 0; @@ -72,6 +72,12 @@ void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) { buf_init_from_buf(out_basename, full_path); } +void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { + buf_init_from_buf(out_full_path, dirname); + buf_append_char(out_full_path, '/'); + buf_append_buf(out_full_path, basename); +} + void os_exec_process(const char *exe, ZigList<const char *> &args, int *return_code, Buf *out_stderr, Buf *out_stdout) { @@ -117,8 +123,8 @@ void os_exec_process(const char *exe, ZigList<const char *> &args, waitpid(pid, return_code, 0); - read_all_fd(stdout_pipe[0], out_stdout); - read_all_fd(stderr_pipe[0], out_stderr); + read_all_fd_stream(stdout_pipe[0], out_stdout); + read_all_fd_stream(stderr_pipe[0], out_stderr); } } @@ -133,3 +139,44 @@ void os_write_file(Buf *full_path, Buf *contents) { if (close(fd) == -1) zig_panic("close failed"); } + +int os_fetch_file(FILE *f, Buf *out_contents) { + int fd = fileno(f); + struct stat st; + if (fstat(fd, &st)) + zig_panic("unable to stat file: %s", strerror(errno)); + off_t big_size = st.st_size; + if (big_size > INT_MAX) + zig_panic("file too big"); + int size = (int)big_size; + + buf_resize(out_contents, size); + ssize_t ret = read(fd, buf_ptr(out_contents), size); + + if (ret != size) + zig_panic("unable to read file: %s", strerror(errno)); + + return 0; +} + +int os_fetch_file_path(Buf *full_path, Buf *out_contents) { + FILE *f = fopen(buf_ptr(full_path), "rb"); + if (!f) + zig_panic("unable to open %s: %s\n", buf_ptr(full_path), strerror(errno)); + int result = os_fetch_file(f, out_contents); + fclose(f); + return result; +} + +int os_get_cwd(Buf *out_cwd) { + int err = ERANGE; + buf_resize(out_cwd, 512); + while (err == ERANGE) { + buf_resize(out_cwd, buf_len(out_cwd) * 2); + err = getcwd(buf_ptr(out_cwd), buf_len(out_cwd)) ? 0 : errno; + } + if (err) + zig_panic("unable to get cwd: %s", strerror(err)); + + return 0; +} diff --git a/src/os.hpp b/src/os.hpp index a17c2b56f1..8bb5d8ea4f 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -11,13 +11,22 @@ #include "list.hpp" #include "buffer.hpp" +#include <stdio.h> + void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached); void os_exec_process(const char *exe, ZigList<const char *> &args, int *return_code, Buf *out_stderr, Buf *out_stdout); void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename); +void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path); void os_write_file(Buf *full_path, Buf *contents); +int os_fetch_file(FILE *file, Buf *out_contents); +int os_fetch_file_path(Buf *full_path, Buf *out_contents); + +int os_get_cwd(Buf *out_cwd); + + #endif diff --git a/src/parser.cpp b/src/parser.cpp index 10f43d26d4..613c559235 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -100,6 +100,8 @@ const char *node_type_str(NodeType node_type) { return "Symbol"; case NodeTypePrefixOpExpr: return "PrefixOpExpr"; + case NodeTypeUse: + return "Use"; } zig_unreachable(); } @@ -241,6 +243,9 @@ void ast_print(AstNode *node, int indent) { fprintf(stderr, "PrimaryExpr Symbol %s\n", buf_ptr(&node->data.symbol)); break; + case NodeTypeUse: + fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.use.path)); + break; } } @@ -1231,7 +1236,36 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b } /* -TopLevelDecl : FnDef | ExternBlock | RootExportDecl +Use : many(Directive) token(Use) token(String) token(Semicolon) +*/ +static AstNode *ast_parse_use(ParseContext *pc, int *token_index, bool mandatory) { + assert(mandatory == false); + + Token *use_kw = &pc->tokens->at(*token_index); + if (use_kw->id != TokenIdKeywordUse) + return nullptr; + *token_index += 1; + + Token *use_name = &pc->tokens->at(*token_index); + *token_index += 1; + ast_expect_token(pc, use_name, TokenIdStringLiteral); + + Token *semicolon = &pc->tokens->at(*token_index); + *token_index += 1; + ast_expect_token(pc, semicolon, TokenIdSemicolon); + + AstNode *node = ast_create_node(NodeTypeUse, use_kw); + + parse_string_literal(pc, use_name, &node->data.use.path); + + node->data.use.directives = pc->directive_list; + pc->directive_list = nullptr; + + return node; +} + +/* +TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use */ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) { for (;;) { @@ -1258,6 +1292,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis continue; } + AstNode *use_node = ast_parse_use(pc, token_index, false); + if (use_node) { + top_level_decls->append(use_node); + continue; + } + if (pc->directive_list->length > 0) { ast_error(directive_token, "invalid directive"); } diff --git a/src/parser.hpp b/src/parser.hpp index 6d99dfbc4c..cc7b3dd0b6 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -35,6 +35,7 @@ enum NodeType { NodeTypeSymbol, NodeTypePrefixOpExpr, NodeTypeFnCallExpr, + NodeTypeUse, }; struct AstNodeRoot { @@ -158,6 +159,11 @@ struct AstNodePrefixOpExpr { AstNode *primary_expr; }; +struct AstNodeUse { + Buf path; + ZigList<AstNode *> *directives; +}; + struct AstNode { enum NodeType type; AstNode *parent; @@ -180,6 +186,7 @@ struct AstNode { AstNodeCastExpr cast_expr; AstNodePrefixOpExpr prefix_op_expr; AstNodeFnCallExpr fn_call_expr; + AstNodeUse use; Buf number; Buf string; Buf symbol; diff --git a/src/semantic_info.hpp b/src/semantic_info.hpp index 13a05b1207..dc87c12930 100644 --- a/src/semantic_info.hpp +++ b/src/semantic_info.hpp @@ -12,15 +12,6 @@ #include "hash_map.hpp" #include "zig_llvm.hpp" -struct FnTableEntry { - LLVMValueRef fn_value; - AstNode *proto_node; - AstNode *fn_def_node; - bool is_extern; - bool internal_linkage; - unsigned calling_convention; -}; - struct TypeTableEntry { LLVMTypeRef type_ref; LLVMZigDIType *di_type; @@ -33,17 +24,35 @@ struct TypeTableEntry { TypeTableEntry *pointer_mut_parent; }; +struct ImportTableEntry { + AstNode *root; + Buf *path; // relative to root_source_dir + LLVMZigDIFile *di_file; +}; + +struct FnTableEntry { + LLVMValueRef fn_value; + AstNode *proto_node; + AstNode *fn_def_node; + bool is_extern; + bool internal_linkage; + unsigned calling_convention; + ImportTableEntry *import_entry; +}; + struct CodeGen { LLVMModuleRef module; - AstNode *root; ZigList<ErrorMsg> errors; LLVMBuilderRef builder; LLVMZigDIBuilder *dbuilder; LLVMZigDICompileUnit *compile_unit; + + // reminder: hash tables must be initialized before use HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table; HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table; HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table; HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table; + HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table; struct { TypeTableEntry *entry_u8; @@ -60,12 +69,10 @@ struct CodeGen { CodeGenBuildType build_type; LLVMTargetMachineRef target_machine; bool is_native_target; - Buf in_file; - Buf in_dir; + Buf *root_source_dir; + Buf *root_out_name; ZigList<LLVMZigDIScope *> block_scopes; - LLVMZigDIFile *di_file; ZigList<FnTableEntry *> fn_defs; - Buf *out_name; OutType out_type; FnTableEntry *cur_fn; bool c_stdint_used; @@ -73,6 +80,8 @@ struct CodeGen { int version_major; int version_minor; int version_patch; + bool verbose; + bool initialized; }; struct TypeNode { diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 3e8a201f52..94f3966f0f 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -180,6 +180,8 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordExport; } else if (mem_eql_str(token_mem, token_len, "as")) { t->cur_tok->id = TokenIdKeywordAs; + } else if (mem_eql_str(token_mem, token_len, "use")) { + t->cur_tok->id = TokenIdKeywordUse; } t->cur_tok = nullptr; @@ -562,6 +564,7 @@ static const char * token_name(Token *token) { case TokenIdKeywordPub: return "Pub"; case TokenIdKeywordExport: return "Export"; case TokenIdKeywordAs: return "As"; + case TokenIdKeywordUse: return "Use"; case TokenIdLParen: return "LParen"; case TokenIdRParen: return "RParen"; case TokenIdComma: return "Comma"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 2c9ad61957..94cbb15d43 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -22,6 +22,7 @@ enum TokenId { TokenIdKeywordPub, TokenIdKeywordExport, TokenIdKeywordAs, + TokenIdKeywordUse, TokenIdLParen, TokenIdRParen, TokenIdComma, |
