refactor code to prepare for multiple files

verbose compiler output is now behind --verbose flag
master
Andrew Kelley 2015-11-30 19:58:53 -07:00
parent ef482ece7c
commit 55b8472374
24 changed files with 474 additions and 287 deletions

View File

@ -22,16 +22,16 @@ include_directories(
)
set(ZIG_SOURCES
"${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
"${CMAKE_SOURCE_DIR}/src/parser.cpp"
"${CMAKE_SOURCE_DIR}/src/analyze.cpp"
"${CMAKE_SOURCE_DIR}/src/codegen.cpp"
"${CMAKE_SOURCE_DIR}/src/buffer.cpp"
"${CMAKE_SOURCE_DIR}/src/error.cpp"
"${CMAKE_SOURCE_DIR}/src/main.cpp"
"${CMAKE_SOURCE_DIR}/src/parser.cpp"
"${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
"${CMAKE_SOURCE_DIR}/src/util.cpp"
"${CMAKE_SOURCE_DIR}/src/codegen.cpp"
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
"${CMAKE_SOURCE_DIR}/src/os.cpp"
"${CMAKE_SOURCE_DIR}/src/util.cpp"
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
)
set(TEST_SOURCES

View File

@ -79,7 +79,9 @@ zig | C equivalent | Description
```
Root : many(TopLevelDecl) token(EOF)
TopLevelDecl : FnDef | ExternBlock | RootExportDecl
TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use
Use : many(Directive) token(Use) token(String) token(Semicolon)
RootExportDecl : many(Directive) token(Export) token(Symbol) token(String) token(Semicolon)

View File

@ -7,7 +7,7 @@ if exists("b:current_syntax")
finish
endif
syn keyword zigKeyword fn return mut const extern unreachable export pub as
syn keyword zigKeyword fn return mut const extern unreachable export pub as use
syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 void
syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
@ -19,6 +19,15 @@ syn region zigCommentBlockDocNest matchgroup=zigCommentBlockDoc start="/\*" end=
syn keyword zigTodo contained TODO XXX
syn match zigEscapeError display contained /\\./
syn match zigEscape display contained /\\\([nrt0\\'"]\|x\x\{2}\)/
syn match zigEscapeUnicode display contained /\\\(u\x\{4}\|U\x\{8}\)/
syn match zigEscapeUnicode display contained /\\u{\x\{1,6}}/
syn match zigStringContinuation display contained /\\\n\s*/
syn region zigString start=+b"+ skip=+\\\\\|\\"+ end=+"+ contains=zigEscape,zigEscapeError,zigStringContinuation
syn region zigString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=zigEscape,zigEscapeUnicode,zigEscapeError,zigStringContinuation,@Spell
syn region zigString start='b\?r\z(#*\)"' end='"\z1' contains=@Spell
let b:current_syntax = "zig"
hi def link zigKeyword Keyword
@ -28,4 +37,8 @@ hi def link zigCommentLineDoc SpecialComment
hi def link zigCommentBlock zigCommentLine
hi def link zigCommentBlockDoc zigCommentLineDoc
hi def link zigTodo Todo
hi def link zigStringContinuation Special
hi def link zigString String
hi def link zigEscape Special
hi def link zigEscapeUnicode zigEscape
hi def link zigEscapeError Error

View File

@ -0,0 +1,5 @@
use "libc.zig";
fn print_text() {
puts("it works!");
}

View File

@ -0,0 +1,5 @@
#link("c")
extern {
fn puts(s: *mut u8) -> i32;
fn exit(code: i32) -> unreachable;
}

View File

@ -0,0 +1,9 @@
export executable "test";
use "libc.zig";
use "foo.zig";
fn _start() -> unreachable {
print_text();
exit(0);
}

View File

@ -0,0 +1,7 @@
#include "mathtest.h"
#include <stdio.h>
int main(int argc, char **argv) {
printf("%d\n", add(42, 1137));
return 0;
}

View File

@ -113,7 +113,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) {
@ -139,6 +139,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;
@ -156,6 +157,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;
@ -190,8 +192,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;
@ -209,6 +211,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:
@ -346,6 +351,7 @@ static void analyze_expression(CodeGen *g, AstNode *node) {
case NodeTypeRootExportDecl:
case NodeTypeExternBlock:
case NodeTypeFnDef:
case NodeTypeUse:
zig_unreachable();
}
}
@ -377,9 +383,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:
@ -400,13 +406,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) {
@ -414,7 +420,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) {
@ -423,88 +429,7 @@ static void analyze_root(CodeGen *g, AstNode *node) {
}
}
static void define_primitive_types(CodeGen *g) {
{
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;
// invalid types are void
g->builtin_types.entry_invalid = entry;
}
{
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
entry->type_ref = LLVMVoidType();
buf_init_from_str(&entry->name, "unreachable");
entry->di_type = g->builtin_types.entry_invalid->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);
}

View File

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

View File

@ -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,168 @@ 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) {
{
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;
// invalid types are void
g->builtin_types.entry_invalid = entry;
}
{
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
entry->type_ref = LLVMVoidType();
buf_init_from_str(&entry->name, "unreachable");
entry->di_type = g->builtin_types.entry_invalid->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 +749,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 +792,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 +826,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 +895,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 +923,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");
}
}

View File

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

View File

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

View File

@ -12,6 +12,7 @@ enum Error {
ErrorNone,
ErrorNoMem,
ErrorInvalidFormat,
ErrorSemanticAnalyzeFail,
};
const char *err_str(int err);

View File

@ -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;
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;
};
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];
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));
Buf in_file_buf = BUF_INIT;
buf_init_from_str(&in_file_buf, b->in_file);
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 {
in_f = fopen(in_file, "rb");
if (!in_f)
zig_panic("unable to open %s for reading: %s\n", in_file, strerror(errno));
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, "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");
} 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;
}
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();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,6 +22,7 @@ enum TokenId {
TokenIdKeywordPub,
TokenIdKeywordExport,
TokenIdKeywordAs,
TokenIdKeywordUse,
TokenIdLParen,
TokenIdRParen,
TokenIdComma,

View File

@ -47,6 +47,7 @@ static void add_simple_case(const char *case_name, const char *source, const cha
test_case->compiler_args.append(tmp_exe_path);
test_case->compiler_args.append("--release");
test_case->compiler_args.append("--strip");
test_case->compiler_args.append("--verbose");
test_cases.append(test_case);
}
@ -70,6 +71,7 @@ static void add_compile_fail_case(const char *case_name, const char *source, int
test_case->compiler_args.append(tmp_exe_path);
test_case->compiler_args.append("--release");
test_case->compiler_args.append("--strip");
test_case->compiler_args.append("--verbose");
test_cases.append(test_case);