ability to generate shared library and h file
parent
f4721857ed
commit
9b477230e0
|
@ -1,4 +1,4 @@
|
|||
export library "math";
|
||||
export library "mathtest";
|
||||
|
||||
export fn add(a: i32, b: i32) -> i32 {
|
||||
return a + b;
|
|
@ -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
|
||||
|
|
177
src/codegen.cpp
177
src/codegen.cpp
|
@ -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 = ¶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<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(¶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 <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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue