ability to generate shared library and h file

master
Andrew Kelley 2015-11-29 17:28:28 -07:00
parent f4721857ed
commit 9b477230e0
3 changed files with 167 additions and 19 deletions

View File

@ -1,4 +1,4 @@
export library "math";
export library "mathtest";
export fn add(a: i32, b: i32) -> i32 {
return a + b;

View File

@ -12,6 +12,7 @@
#include <assert.h>
#include <stdint.h>
#include <ctype.h>
#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

View File

@ -77,7 +77,8 @@ struct CodeGen {
ZigList<FnTableEntry *> 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 = &param_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<LLVMValueRef>(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(&param_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 <stdint.h>\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);
}
}