From 9b477230e0142e46d1276873d739cd7bac8b4e86 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Nov 2015 17:28:28 -0700 Subject: [PATCH] ability to generate shared library and h file --- example/{math.zig => mathtest.zig} | 2 +- src/buffer.hpp | 7 ++ src/codegen.cpp | 177 ++++++++++++++++++++++++++--- 3 files changed, 167 insertions(+), 19 deletions(-) rename example/{math.zig => mathtest.zig} (68%) diff --git a/example/math.zig b/example/mathtest.zig similarity index 68% rename from example/math.zig rename to example/mathtest.zig index af5b9a706a..2736f8b26d 100644 --- a/example/math.zig +++ b/example/mathtest.zig @@ -1,4 +1,4 @@ -export library "math"; +export library "mathtest"; export fn add(a: i32, b: i32) -> i32 { return a + b; diff --git a/src/buffer.hpp b/src/buffer.hpp index 6e455d9d84..d95036b2b0 100644 --- a/src/buffer.hpp +++ b/src/buffer.hpp @@ -12,6 +12,7 @@ #include #include +#include #define BUF_INIT {{0}} @@ -147,4 +148,10 @@ static inline uint32_t buf_hash(Buf *buf) { return h; } +static inline void buf_upcase(Buf *buf) { + for (int i = 0; i < buf_len(buf); i += 1) { + buf_ptr(buf)[i] = toupper(buf_ptr(buf)[i]); + } +} + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 35780efa4c..74ca4a322c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -77,7 +77,8 @@ struct CodeGen { ZigList fn_defs; Buf *out_name; OutType out_type; - LLVMValueRef cur_fn; + FnTableEntry *cur_fn; + bool c_stdint_used; }; struct TypeNode { @@ -87,6 +88,7 @@ struct TypeNode { struct FnDefNode { bool add_implicit_return; bool skip; + LLVMValueRef *params; }; struct CodeGenNode { @@ -639,6 +641,23 @@ static LLVMValueRef find_or_create_string(CodeGen *g, Buf *str) { return global_value; } +static LLVMValueRef get_variable_value(CodeGen *g, Buf *name) { + assert(g->cur_fn->proto_node->type == NodeTypeFnProto); + int param_count = g->cur_fn->proto_node->data.fn_proto.params.length; + for (int i = 0; i < param_count; i += 1) { + AstNode *param_decl_node = g->cur_fn->proto_node->data.fn_proto.params.at(i); + assert(param_decl_node->type == NodeTypeParamDecl); + Buf *param_name = ¶m_decl_node->data.param_decl.name; + if (buf_eql_buf(name, param_name)) { + CodeGenNode *codegen_node = g->cur_fn->fn_def_node->codegen_node; + assert(codegen_node); + FnDefNode *codegen_fn_def = &codegen_node->data.fn_def_node; + return codegen_fn_def->params[i]; + } + } + zig_unreachable(); +} + static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); @@ -797,9 +816,9 @@ static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) { LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1); // block for when val1 == true - LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn, "BoolAndTrue"); + LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndTrue"); // block for when val1 == false (don't even evaluate the second part) - LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn, "BoolAndFalse"); + LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndFalse"); LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(val1)); add_debug_source_node(g, node); @@ -828,9 +847,9 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) { LLVMValueRef val1 = gen_expr(g, expr_node->data.bin_op_expr.op1); // block for when val1 == false - LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn, "BoolOrFalse"); + LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrFalse"); // block for when val1 == true (don't even evaluate the second part) - LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn, "BoolOrTrue"); + LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrTrue"); LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(val1)); add_debug_source_node(g, expr_node); @@ -933,6 +952,11 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { LLVMValueRef ptr_val = LLVMBuildInBoundsGEP(g->builder, str_val, indices, 2, ""); return ptr_val; } + case NodeTypeSymbol: + { + Buf *name = &node->data.symbol; + return get_variable_value(g, name); + } case NodeTypeRoot: case NodeTypeRootExportDecl: case NodeTypeFnProto: @@ -943,7 +967,6 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeBlock: case NodeTypeExternBlock: case NodeTypeDirective: - case NodeTypeSymbol: zig_unreachable(); } zig_unreachable(); @@ -1047,7 +1070,7 @@ void code_gen(CodeGen *g) { FnTableEntry *fn_table_entry = g->fn_defs.at(i); AstNode *fn_def_node = fn_table_entry->fn_def_node; LLVMValueRef fn = fn_table_entry->fn_value; - g->cur_fn = fn; + g->cur_fn = fn_table_entry; AstNode *proto_node = fn_table_entry->proto_node; assert(proto_node->type == NodeTypeFnProto); @@ -1072,7 +1095,12 @@ void code_gen(CodeGen *g) { CodeGenNode *codegen_node = fn_def_node->codegen_node; assert(codegen_node); - bool add_implicit_return = codegen_node->data.fn_def_node.add_implicit_return; + + FnDefNode *codegen_fn_def = &codegen_node->data.fn_def_node; + codegen_fn_def->params = allocate(LLVMCountParams(fn)); + 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); g->block_scopes.pop(); @@ -1216,15 +1244,113 @@ static Buf *get_dynamic_linker(CodeGen *g) { } } -/* +static Buf *to_c_type(CodeGen *g, AstNode *type_node) { + assert(type_node->type == NodeTypeType); + assert(type_node->codegen_node); -# static link into libfoo.a -ar cq libfoo.a foo1.o foo2.o + TypeTableEntry *type_entry = type_node->codegen_node->data.type_node.entry; + assert(type_entry); -# dynamic link into libfoo.so -gcc -fPIC -g -Werror -pedantic -shared -Wl,-soname,libsoundio.so.1 -o libsoundio.so.1.0.3 foo1.o foo2.o -ljack -lpulse -lasound -lpthread + switch (type_entry->id) { + case TypeIdUserDefined: + zig_panic("TODO"); + break; + case TypeIdPointer: + zig_panic("TODO"); + break; + case TypeIdU8: + g->c_stdint_used = true; + return buf_create_from_str("uint8_t"); + case TypeIdI32: + g->c_stdint_used = true; + return buf_create_from_str("int32_t"); + case TypeIdVoid: + zig_panic("TODO"); + break; + case TypeIdUnreachable: + zig_panic("TODO"); + break; + } + zig_unreachable(); +} + +static void generate_h_file(CodeGen *g) { + Buf *h_file_out_path = buf_sprintf("%s.h", buf_ptr(g->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_upcase(export_macro); + + Buf *extern_c_macro = buf_sprintf("%s_EXTERN_C", buf_ptr(g->out_name)); + buf_upcase(extern_c_macro); + + Buf h_buf = BUF_INIT; + buf_resize(&h_buf, 0); + for (int fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) { + FnTableEntry *fn_table_entry = g->fn_defs.at(fn_def_i); + AstNode *proto_node = fn_table_entry->proto_node; + assert(proto_node->type == NodeTypeFnProto); + AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; + + if (fn_proto->visib_mod != FnProtoVisibModExport) + continue; + + buf_appendf(&h_buf, "%s %s %s(", + buf_ptr(export_macro), + buf_ptr(to_c_type(g, fn_proto->return_type)), + buf_ptr(&fn_proto->name)); + + if (fn_proto->params.length) { + for (int param_i = 0; param_i < fn_proto->params.length; param_i += 1) { + AstNode *param_decl_node = fn_proto->params.at(param_i); + AstNode *param_type = param_decl_node->data.param_decl.type; + buf_appendf(&h_buf, "%s %s", + buf_ptr(to_c_type(g, param_type)), + buf_ptr(¶m_decl_node->data.param_decl.name)); + if (param_i < fn_proto->params.length - 1) + buf_appendf(&h_buf, ", "); + } + buf_appendf(&h_buf, ");\n"); + } else { + buf_appendf(&h_buf, "void);\n"); + } + } + + Buf *ifdef_dance_name = buf_sprintf("%s_%s_H", buf_ptr(g->out_name), buf_ptr(g->out_name)); + buf_upcase(ifdef_dance_name); + + fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name)); + fprintf(out_h, "#define %s\n\n", buf_ptr(ifdef_dance_name)); + + if (g->c_stdint_used) + fprintf(out_h, "#include \n"); + + fprintf(out_h, "\n"); + + fprintf(out_h, "#ifdef __cplusplus\n"); + fprintf(out_h, "#define %s extern \"C\"\n", buf_ptr(extern_c_macro)); + fprintf(out_h, "#else\n"); + fprintf(out_h, "#define %s\n", buf_ptr(extern_c_macro)); + fprintf(out_h, "#endif\n"); + fprintf(out_h, "\n"); + fprintf(out_h, "#if defined(_WIN32)\n"); + fprintf(out_h, "#define %s %s __declspec(dllimport)\n", buf_ptr(export_macro), buf_ptr(extern_c_macro)); + fprintf(out_h, "#else\n"); + fprintf(out_h, "#define %s %s __attribute__((visibility (\"default\")))\n", + buf_ptr(export_macro), buf_ptr(extern_c_macro)); + fprintf(out_h, "#endif\n"); + fprintf(out_h, "\n"); + + fprintf(out_h, "%s", buf_ptr(&h_buf)); + + fprintf(out_h, "\n#endif\n"); + + if (fclose(out_h)) + zig_panic("unable to close h file: %s", strerror(errno)); +} -*/ void code_gen_link(CodeGen *g, const char *out_file) { if (!out_file) { out_file = buf_ptr(g->out_name); @@ -1250,6 +1376,9 @@ void code_gen_link(CodeGen *g, const char *out_file) { if (g->out_type == OutTypeLib && g->is_static) { // invoke `ar` + // example: + // # static link into libfoo.a + // ar cq libfoo.a foo1.o foo2.o zig_panic("TODO invoke ar"); return; } @@ -1260,10 +1389,6 @@ void code_gen_link(CodeGen *g, const char *out_file) { args.append("-static"); } - if (g->out_type == OutTypeLib) { - zig_panic("TODO add ld commands for shared library"); - } - char *ZIG_NATIVE_DYNAMIC_LINKER = getenv("ZIG_NATIVE_DYNAMIC_LINKER"); if (g->is_native_target && ZIG_NATIVE_DYNAMIC_LINKER) { if (ZIG_NATIVE_DYNAMIC_LINKER[0] != 0) { @@ -1275,6 +1400,18 @@ void code_gen_link(CodeGen *g, const char *out_file) { args.append(buf_ptr(get_dynamic_linker(g))); } + if (g->out_type == OutTypeLib) { + int major = 1; + int minor = 0; + int patch = 0; + Buf *out_lib_so = buf_sprintf("lib%s.so.%d.%d.%d", buf_ptr(g->out_name), major, minor, patch); + Buf *soname = buf_sprintf("lib%s.so.%d", buf_ptr(g->out_name), major); + args.append("-shared"); + args.append("-soname"); + args.append(buf_ptr(soname)); + out_file = buf_ptr(out_lib_so); + } + args.append("-o"); args.append(out_file); @@ -1291,6 +1428,10 @@ void code_gen_link(CodeGen *g, const char *out_file) { } os_spawn_process("ld", args, false); + + if (g->out_type == OutTypeLib) { + generate_h_file(g); + } }