zig/src/codegen.cpp

3766 lines
148 KiB
C++
Raw Normal View History

/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "codegen.hpp"
#include "hash_map.hpp"
#include "zig_llvm.hpp"
2015-11-25 04:51:36 +08:00
#include "os.hpp"
2015-11-25 10:07:33 +08:00
#include "config.h"
#include "error.hpp"
#include "analyze.hpp"
#include "errmsg.hpp"
#include "parseh.hpp"
#include "ast_render.hpp"
2015-12-01 00:14:58 +08:00
#include <stdio.h>
2015-12-01 05:43:09 +08:00
#include <errno.h>
2015-11-25 10:07:33 +08:00
CodeGen *codegen_create(Buf *root_source_dir) {
CodeGen *g = allocate<CodeGen>(1);
g->link_table.init(32);
g->import_table.init(32);
g->builtin_fn_table.init(32);
g->primitive_type_table.init(32);
g->unresolved_top_level_decls.init(32);
g->fn_type_table.init(32);
2015-11-25 13:32:26 +08:00
g->build_type = CodeGenBuildTypeDebug;
g->root_source_dir = root_source_dir;
g->next_error_index = 1;
g->error_value_count = 1;
return g;
}
void codegen_set_clang_argv(CodeGen *g, const char **args, int len) {
g->clang_argv = args;
g->clang_argv_len = len;
}
2015-11-25 13:32:26 +08:00
void codegen_set_build_type(CodeGen *g, CodeGenBuildType build_type) {
g->build_type = build_type;
}
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_errmsg_color(CodeGen *g, ErrColor err_color) {
g->err_color = err_color;
}
2015-11-25 13:32:26 +08:00
void codegen_set_strip(CodeGen *g, bool strip) {
g->strip_debug_symbols = strip;
}
void codegen_set_out_type(CodeGen *g, OutType out_type) {
g->out_type = out_type;
}
void codegen_set_out_name(CodeGen *g, Buf *out_name) {
g->root_out_name = out_name;
}
void codegen_set_libc_path(CodeGen *g, Buf *libc_path) {
g->libc_path = libc_path;
}
2016-02-01 09:32:07 +08:00
void codegen_add_lib_dir(CodeGen *g, const char *dir) {
g->lib_dirs.append(dir);
}
2015-12-01 00:14:58 +08:00
static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node);
static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node, TypeTableEntry **out_type_entry);
static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue);
2016-01-02 15:06:06 +08:00
static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl,
bool unwrap_maybe, LLVMValueRef *init_val, TypeTableEntry **init_val_type);
2016-01-02 15:06:06 +08:00
static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op,
LLVMValueRef target_ref, LLVMValueRef value,
TypeTableEntry *op1_type, TypeTableEntry *op2_type);
2016-02-01 17:11:46 +08:00
static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref);
2016-01-14 09:15:51 +08:00
static TypeTableEntry *get_type_for_type_node(AstNode *node) {
Expr *expr = get_resolved_expr(node);
assert(expr->type_entry->id == TypeTableEntryIdMetaType);
ConstExprValue *const_val = &expr->const_val;
assert(const_val->ok);
return const_val->data.x_type;
2015-11-25 10:07:33 +08:00
}
static void add_debug_source_node(CodeGen *g, AstNode *node) {
2016-01-26 07:37:45 +08:00
assert(node->block_context);
LLVMZigSetCurrentDebugLocation(g->builder, node->line + 1, node->column + 1, node->block_context->di_scope);
}
2015-12-02 12:19:38 +08:00
static TypeTableEntry *get_expr_type(AstNode *node) {
2016-01-23 06:31:35 +08:00
return get_resolved_expr(node)->type_entry;
2015-12-02 12:19:38 +08:00
}
static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_node) {
TypeTableEntry *type_entry = get_type_for_type_node(type_node);
if (handle_is_ptr(type_entry)) {
return get_pointer_to_type(g, type_entry, true);
} else {
return type_entry;
}
2016-01-18 19:34:26 +08:00
}
2016-01-26 14:21:13 +08:00
enum AddSubMul {
AddSubMulAdd = 0,
AddSubMulSub = 1,
AddSubMulMul = 2,
};
static int bits_index(int size_in_bits) {
switch (size_in_bits) {
case 8:
return 0;
case 16:
return 1;
case 32:
return 2;
case 64:
return 3;
default:
zig_unreachable();
}
}
static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
const char *signed_name, const char *unsigned_name)
{
const char *signed_str = type_entry->data.integral.is_signed ? signed_name : unsigned_name;
Buf *llvm_name = buf_sprintf("llvm.%s.with.overflow.i%" PRIu64, signed_str, type_entry->size_in_bits);
LLVMTypeRef return_elem_types[] = {
type_entry->type_ref,
LLVMInt1Type(),
};
LLVMTypeRef param_types[] = {
type_entry->type_ref,
type_entry->type_ref,
};
LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type);
assert(LLVMGetIntrinsicID(fn_val));
return fn_val;
}
static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry, AddSubMul add_sub_mul) {
assert(type_entry->id == TypeTableEntryIdInt);
// [0-signed,1-unsigned][0-add,1-sub,2-mul][0-8,1-16,2-32,3-64]
int index0 = type_entry->data.integral.is_signed ? 0 : 1;
int index1 = add_sub_mul;
int index2 = bits_index(type_entry->size_in_bits);
LLVMValueRef *fn = &g->int_overflow_fns[index0][index1][index2];
if (*fn) {
return *fn;
}
switch (add_sub_mul) {
case AddSubMulAdd:
*fn = get_arithmetic_overflow_fn(g, type_entry, "sadd", "uadd");
break;
case AddSubMulSub:
*fn = get_arithmetic_overflow_fn(g, type_entry, "ssub", "usub");
break;
case AddSubMulMul:
*fn = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul");
break;
}
return *fn;
}
static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeFnCallExpr);
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
assert(fn_ref_expr->type == NodeTypeSymbol);
BuiltinFnEntry *builtin_fn = node->data.fn_call_expr.builtin_fn;
switch (builtin_fn->id) {
case BuiltinFnIdInvalid:
2016-01-14 09:15:51 +08:00
case BuiltinFnIdTypeof:
2016-01-27 07:00:39 +08:00
case BuiltinFnIdCInclude:
case BuiltinFnIdCDefine:
case BuiltinFnIdCUndef:
zig_unreachable();
case BuiltinFnIdAddWithOverflow:
case BuiltinFnIdSubWithOverflow:
case BuiltinFnIdMulWithOverflow:
{
int fn_call_param_count = node->data.fn_call_expr.params.length;
assert(fn_call_param_count == 4);
TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
2016-01-26 14:21:13 +08:00
AddSubMul add_sub_mul;
if (builtin_fn->id == BuiltinFnIdAddWithOverflow) {
2016-01-26 14:21:13 +08:00
add_sub_mul = AddSubMulAdd;
} else if (builtin_fn->id == BuiltinFnIdSubWithOverflow) {
2016-01-26 14:21:13 +08:00
add_sub_mul = AddSubMulSub;
} else if (builtin_fn->id == BuiltinFnIdMulWithOverflow) {
2016-01-26 14:21:13 +08:00
add_sub_mul = AddSubMulMul;
} else {
zig_unreachable();
}
2016-01-26 14:21:13 +08:00
LLVMValueRef fn_val = get_int_overflow_fn(g, int_type, add_sub_mul);
LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(1));
LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(2));
LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3));
LLVMValueRef params[] = {
op1,
op2,
};
add_debug_source_node(g, node);
LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
LLVMBuildStore(g->builder, result, ptr_result);
return overflow_bit;
}
2016-01-09 17:16:54 +08:00
case BuiltinFnIdMemcpy:
{
int fn_call_param_count = node->data.fn_call_expr.params.length;
assert(fn_call_param_count == 3);
AstNode *dest_node = node->data.fn_call_expr.params.at(0);
TypeTableEntry *dest_type = get_expr_type(dest_node);
LLVMValueRef dest_ptr = gen_expr(g, dest_node);
LLVMValueRef src_ptr = gen_expr(g, node->data.fn_call_expr.params.at(1));
LLVMValueRef len_val = gen_expr(g, node->data.fn_call_expr.params.at(2));
LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
add_debug_source_node(g, node);
LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, ptr_u8, "");
uint64_t align_in_bytes = dest_type->data.pointer.child_type->align_in_bits / 8;
LLVMValueRef params[] = {
dest_ptr_casted, // dest pointer
src_ptr_casted, // source pointer
len_val, // byte count
LLVMConstInt(LLVMInt32Type(), align_in_bytes, false), // align in bytes
LLVMConstNull(LLVMInt1Type()), // is volatile
};
LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 5, "");
return nullptr;
}
case BuiltinFnIdMemset:
{
int fn_call_param_count = node->data.fn_call_expr.params.length;
assert(fn_call_param_count == 3);
AstNode *dest_node = node->data.fn_call_expr.params.at(0);
TypeTableEntry *dest_type = get_expr_type(dest_node);
LLVMValueRef dest_ptr = gen_expr(g, dest_node);
LLVMValueRef char_val = gen_expr(g, node->data.fn_call_expr.params.at(1));
LLVMValueRef len_val = gen_expr(g, node->data.fn_call_expr.params.at(2));
LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
add_debug_source_node(g, node);
LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
uint64_t align_in_bytes = dest_type->data.pointer.child_type->align_in_bits / 8;
LLVMValueRef params[] = {
dest_ptr_casted, // dest pointer
char_val, // source pointer
len_val, // byte count
LLVMConstInt(LLVMInt32Type(), align_in_bytes, false), // align in bytes
LLVMConstNull(LLVMInt1Type()), // is volatile
};
LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 5, "");
return nullptr;
}
2016-01-14 09:15:51 +08:00
case BuiltinFnIdSizeof:
case BuiltinFnIdMinValue:
case BuiltinFnIdMaxValue:
case BuiltinFnIdMemberCount:
// caught by constant expression eval codegen
zig_unreachable();
}
zig_unreachable();
}
2016-01-12 11:33:06 +08:00
static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntry *enum_type,
AstNode *arg_node)
{
assert(node->type == NodeTypeFieldAccessExpr);
uint64_t value = node->data.field_access_expr.type_enum_field->value;
LLVMTypeRef tag_type_ref = enum_type->data.enumeration.tag_type->type_ref;
LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, value, false);
if (enum_type->data.enumeration.gen_field_count == 0) {
return tag_value;
} else {
TypeTableEntry *arg_node_type = nullptr;
LLVMValueRef new_union_val = gen_expr(g, arg_node);
if (arg_node) {
arg_node_type = get_expr_type(arg_node);
new_union_val = gen_expr(g, arg_node);
} else {
arg_node_type = g->builtin_types.entry_void;
}
LLVMValueRef tmp_struct_ptr = node->data.field_access_expr.resolved_struct_val_expr.ptr;
// populate the new tag value
add_debug_source_node(g, node);
LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
LLVMBuildStore(g->builder, tag_value, tag_field_ptr);
if (arg_node_type->id != TypeTableEntryIdVoid) {
// populate the union value
TypeTableEntry *union_val_type = get_expr_type(arg_node);
LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr,
LLVMPointerType(union_val_type->type_ref, 0), "");
gen_assign_raw(g, arg_node, BinOpTypeAssign, bitcasted_union_field_ptr, new_union_val,
union_val_type, union_val_type);
}
return tmp_struct_ptr;
}
}
2016-01-23 17:14:01 +08:00
static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeTableEntry *actual_type,
TypeTableEntry *wanted_type, LLVMValueRef expr_val)
{
2016-02-01 12:22:05 +08:00
assert(actual_type->id == wanted_type->id);
2016-01-23 17:14:01 +08:00
if (actual_type->size_in_bits == wanted_type->size_in_bits) {
return expr_val;
} else if (actual_type->size_in_bits < wanted_type->size_in_bits) {
2016-02-01 12:22:05 +08:00
if (actual_type->id == TypeTableEntryIdFloat) {
2016-01-23 17:14:01 +08:00
add_debug_source_node(g, source_node);
2016-02-01 12:22:05 +08:00
return LLVMBuildFPExt(g->builder, expr_val, wanted_type->type_ref, "");
} else if (actual_type->id == TypeTableEntryIdInt) {
if (actual_type->data.integral.is_signed) {
add_debug_source_node(g, source_node);
return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, "");
} else {
add_debug_source_node(g, source_node);
return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, "");
}
2016-01-23 17:14:01 +08:00
} else {
2016-02-01 12:22:05 +08:00
zig_unreachable();
}
} else if (actual_type->size_in_bits > wanted_type->size_in_bits) {
if (actual_type->id == TypeTableEntryIdFloat) {
2016-01-23 17:14:01 +08:00
add_debug_source_node(g, source_node);
2016-02-01 12:22:05 +08:00
return LLVMBuildFPTrunc(g->builder, expr_val, wanted_type->type_ref, "");
} else if (actual_type->id == TypeTableEntryIdInt) {
add_debug_source_node(g, source_node);
return LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, "");
} else {
zig_unreachable();
2016-01-23 17:14:01 +08:00
}
} else {
2016-02-01 12:22:05 +08:00
zig_unreachable();
2016-01-23 17:14:01 +08:00
}
}
static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeFnCallExpr);
AstNode *expr_node = node->data.fn_call_expr.params.at(0);
LLVMValueRef expr_val = gen_expr(g, expr_node);
TypeTableEntry *actual_type = get_expr_type(expr_node);
TypeTableEntry *wanted_type = get_expr_type(node);
2016-01-23 06:31:35 +08:00
AstNodeFnCallExpr *cast_expr = &node->data.fn_call_expr;
switch (cast_expr->cast_op) {
case CastOpNoCast:
zig_unreachable();
case CastOpNoop:
return expr_val;
2016-01-23 15:34:47 +08:00
case CastOpErrToInt:
assert(actual_type->id == TypeTableEntryIdErrorUnion);
2016-01-23 15:34:47 +08:00
if (actual_type->data.error.child_type->size_in_bits == 0) {
2016-01-23 17:14:01 +08:00
return gen_widen_or_shorten(g, node, g->err_tag_type, wanted_type, expr_val);
2016-01-23 15:34:47 +08:00
} else {
zig_panic("TODO");
}
2016-01-23 06:31:35 +08:00
case CastOpMaybeWrap:
{
assert(cast_expr->tmp_ptr);
assert(wanted_type->id == TypeTableEntryIdMaybe);
assert(actual_type);
TypeTableEntry *child_type = wanted_type->data.maybe.child_type;
2016-01-23 06:31:35 +08:00
if (child_type->id == TypeTableEntryIdPointer ||
child_type->id == TypeTableEntryIdFn)
{
return expr_val;
} else {
add_debug_source_node(g, node);
LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, "");
gen_assign_raw(g, node, BinOpTypeAssign,
val_ptr, expr_val, child_type, actual_type);
add_debug_source_node(g, node);
LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, "");
LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr);
}
2016-01-23 06:31:35 +08:00
return cast_expr->tmp_ptr;
}
2016-01-23 17:14:01 +08:00
case CastOpErrorWrap:
{
assert(wanted_type->id == TypeTableEntryIdErrorUnion);
TypeTableEntry *child_type = wanted_type->data.error.child_type;
LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref);
if (child_type->size_in_bits == 0) {
return ok_err_val;
} else {
assert(cast_expr->tmp_ptr);
assert(wanted_type->id == TypeTableEntryIdErrorUnion);
assert(actual_type);
add_debug_source_node(g, node);
LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, "");
LLVMBuildStore(g->builder, ok_err_val, err_tag_ptr);
LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, "");
gen_assign_raw(g, node, BinOpTypeAssign,
payload_ptr, expr_val, child_type, actual_type);
return cast_expr->tmp_ptr;
}
2016-01-23 17:14:01 +08:00
}
case CastOpPureErrorWrap:
assert(wanted_type->id == TypeTableEntryIdErrorUnion);
if (wanted_type->data.error.child_type->size_in_bits == 0) {
return expr_val;
} else {
zig_panic("TODO");
}
2016-01-23 06:31:35 +08:00
case CastOpPtrToInt:
add_debug_source_node(g, node);
return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
case CastOpIntToPtr:
add_debug_source_node(g, node);
return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, "");
2016-01-23 06:31:35 +08:00
case CastOpPointerReinterpret:
add_debug_source_node(g, node);
return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, "");
2016-02-01 12:22:05 +08:00
case CastOpWidenOrShorten:
2016-01-23 17:14:01 +08:00
return gen_widen_or_shorten(g, node, actual_type, wanted_type, expr_val);
2016-01-23 06:31:35 +08:00
case CastOpToUnknownSizeArray:
{
assert(cast_expr->tmp_ptr);
assert(wanted_type->id == TypeTableEntryIdStruct);
assert(wanted_type->data.structure.is_unknown_size_array);
TypeTableEntry *pointer_type = wanted_type->data.structure.fields[0].type_entry;
add_debug_source_node(g, node);
2016-01-23 06:31:35 +08:00
LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, "");
LLVMValueRef expr_bitcast = LLVMBuildBitCast(g->builder, expr_val, pointer_type->type_ref, "");
LLVMBuildStore(g->builder, expr_bitcast, ptr_ptr);
2016-01-23 06:31:35 +08:00
LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, "");
LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref,
actual_type->data.array.len, false);
LLVMBuildStore(g->builder, len_val, len_ptr);
return cast_expr->tmp_ptr;
}
case CastOpIntToFloat:
assert(actual_type->id == TypeTableEntryIdInt);
if (actual_type->data.integral.is_signed) {
add_debug_source_node(g, node);
return LLVMBuildSIToFP(g->builder, expr_val, wanted_type->type_ref, "");
} else {
add_debug_source_node(g, node);
return LLVMBuildUIToFP(g->builder, expr_val, wanted_type->type_ref, "");
}
case CastOpFloatToInt:
assert(wanted_type->id == TypeTableEntryIdInt);
if (wanted_type->data.integral.is_signed) {
add_debug_source_node(g, node);
return LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, "");
} else {
add_debug_source_node(g, node);
return LLVMBuildFPToUI(g->builder, expr_val, wanted_type->type_ref, "");
}
2016-01-23 06:31:35 +08:00
}
zig_unreachable();
}
2016-01-12 11:33:06 +08:00
2016-01-23 06:31:35 +08:00
2015-11-30 05:39:11 +08:00
static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeFnCallExpr);
if (node->data.fn_call_expr.is_builtin) {
return gen_builtin_fn_call_expr(g, node);
2016-01-23 06:31:35 +08:00
} else if (node->data.fn_call_expr.cast_op != CastOpNoCast) {
return gen_cast_expr(g, node);
}
FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
2016-01-05 07:57:22 +08:00
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
TypeTableEntry *struct_type = nullptr;
AstNode *first_param_expr = nullptr;
2016-01-05 07:57:22 +08:00
if (fn_ref_expr->type == NodeTypeFieldAccessExpr) {
first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr;
struct_type = get_expr_type(first_param_expr);
if (struct_type->id == TypeTableEntryIdStruct) {
fn_table_entry = node->data.fn_call_expr.fn_entry;
} else if (struct_type->id == TypeTableEntryIdPointer) {
assert(struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct);
fn_table_entry = node->data.fn_call_expr.fn_entry;
} else if (struct_type->id == TypeTableEntryIdMetaType) {
TypeTableEntry *child_type = get_type_for_type_node(first_param_expr);
if (child_type->id == TypeTableEntryIdEnum) {
int param_count = node->data.fn_call_expr.params.length;
AstNode *arg1_node;
if (param_count == 1) {
arg1_node = node->data.fn_call_expr.params.at(0);
} else {
assert(param_count == 0);
arg1_node = nullptr;
}
return gen_enum_value_expr(g, fn_ref_expr, child_type, arg1_node);
} else if (child_type->id == TypeTableEntryIdStruct) {
struct_type = nullptr;
first_param_expr = nullptr;
fn_table_entry = node->data.fn_call_expr.fn_entry;
2016-01-12 11:33:06 +08:00
} else {
zig_unreachable();
2016-01-12 11:33:06 +08:00
}
} else {
zig_unreachable();
}
2016-01-05 07:57:22 +08:00
}
2015-11-30 05:39:11 +08:00
2016-01-19 07:42:45 +08:00
TypeTableEntry *fn_type;
LLVMValueRef fn_val;
if (fn_table_entry) {
fn_val = fn_table_entry->fn_value;
fn_type = fn_table_entry->type_entry;
} else {
fn_val = gen_expr(g, fn_ref_expr);
fn_type = get_expr_type(fn_ref_expr);
}
TypeTableEntry *src_return_type = fn_type->data.fn.fn_type_id.return_type;
2016-01-05 07:57:22 +08:00
int fn_call_param_count = node->data.fn_call_expr.params.length;
bool first_arg_ret = handle_is_ptr(src_return_type);
int actual_param_count = fn_call_param_count + (struct_type ? 1 : 0) + (first_arg_ret ? 1 : 0);
bool is_var_args = fn_type->data.fn.fn_type_id.is_var_args;
2015-11-30 05:39:11 +08:00
2015-12-04 05:29:19 +08:00
// don't really include void values
2016-01-19 07:42:45 +08:00
LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);
2015-12-04 05:29:19 +08:00
int gen_param_index = 0;
if (first_arg_ret) {
gen_param_values[gen_param_index] = node->data.fn_call_expr.tmp_ptr;
gen_param_index += 1;
}
2016-01-05 07:57:22 +08:00
if (struct_type) {
gen_param_values[gen_param_index] = gen_expr(g, first_param_expr);
gen_param_index += 1;
}
for (int i = 0; i < fn_call_param_count; i += 1) {
2015-11-30 05:39:11 +08:00
AstNode *expr_node = node->data.fn_call_expr.params.at(i);
2015-12-04 05:29:19 +08:00
LLVMValueRef param_value = gen_expr(g, expr_node);
2016-01-19 07:42:45 +08:00
TypeTableEntry *param_type = get_expr_type(expr_node);
if (is_var_args || param_type->size_in_bits > 0) {
2015-12-04 05:29:19 +08:00
gen_param_values[gen_param_index] = param_value;
gen_param_index += 1;
}
2015-11-28 15:40:54 +08:00
}
2015-11-30 05:39:11 +08:00
add_debug_source_node(g, node);
2016-01-19 07:42:45 +08:00
LLVMValueRef result = LLVMZigBuildCall(g->builder, fn_val,
gen_param_values, gen_param_index, fn_type->data.fn.calling_convention, "");
2015-11-30 05:39:11 +08:00
2016-01-28 05:18:20 +08:00
if (src_return_type->id == TypeTableEntryIdUnreachable) {
2015-11-30 05:39:11 +08:00
return LLVMBuildUnreachable(g->builder);
} else if (first_arg_ret) {
return node->data.fn_call_expr.tmp_ptr;
} else if (src_return_type->size_in_bits == 0) {
return nullptr;
2015-11-30 05:39:11 +08:00
} else {
return result;
}
}
static LLVMValueRef gen_array_base_ptr(CodeGen *g, AstNode *node) {
TypeTableEntry *type_entry = get_expr_type(node);
LLVMValueRef array_ptr;
if (node->type == NodeTypeFieldAccessExpr) {
array_ptr = gen_field_access_expr(g, node, true);
if (type_entry->id == TypeTableEntryIdPointer) {
// we have a double pointer so we must dereference it once
add_debug_source_node(g, node);
array_ptr = LLVMBuildLoad(g->builder, array_ptr, "");
}
} else {
array_ptr = gen_expr(g, node);
}
2016-01-16 09:41:19 +08:00
assert(!array_ptr || LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
return array_ptr;
}
static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMValueRef array_ptr,
TypeTableEntry *array_type, LLVMValueRef subscript_value)
{
2015-12-09 05:47:17 +08:00
assert(subscript_value);
if (array_type->size_in_bits == 0) {
2016-01-16 09:41:19 +08:00
return nullptr;
}
if (array_type->id == TypeTableEntryIdArray) {
LLVMValueRef indices[] = {
2016-01-19 12:13:14 +08:00
LLVMConstNull(g->builtin_types.entry_isize->type_ref),
subscript_value
};
add_debug_source_node(g, source_node);
return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
} else if (array_type->id == TypeTableEntryIdPointer) {
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
LLVMValueRef indices[] = {
subscript_value
};
add_debug_source_node(g, source_node);
return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, "");
} else if (array_type->id == TypeTableEntryIdStruct) {
assert(array_type->data.structure.is_unknown_size_array);
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
add_debug_source_node(g, source_node);
LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, 0, "");
LLVMValueRef ptr = LLVMBuildLoad(g->builder, ptr_ptr, "");
return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, "");
} else {
zig_unreachable();
}
2015-12-09 06:31:43 +08:00
}
static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeArrayAccessExpr);
AstNode *array_expr_node = node->data.array_access_expr.array_ref_expr;
TypeTableEntry *array_type = get_expr_type(array_expr_node);
LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node);
LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript);
return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value);
}
static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **out_type_entry) {
assert(node->type == NodeTypeFieldAccessExpr);
AstNode *struct_expr_node = node->data.field_access_expr.struct_expr;
LLVMValueRef struct_ptr;
if (struct_expr_node->type == NodeTypeSymbol) {
2016-01-26 07:37:45 +08:00
VariableTableEntry *var = find_variable(struct_expr_node->block_context,
&struct_expr_node->data.symbol_expr.symbol);
assert(var);
if (var->is_ptr && var->type->id == TypeTableEntryIdPointer) {
add_debug_source_node(g, node);
struct_ptr = LLVMBuildLoad(g->builder, var->value_ref, "");
} else {
struct_ptr = var->value_ref;
}
} else if (struct_expr_node->type == NodeTypeFieldAccessExpr) {
struct_ptr = gen_field_access_expr(g, struct_expr_node, true);
TypeTableEntry *field_type = get_expr_type(struct_expr_node);
if (field_type->id == TypeTableEntryIdPointer) {
// we have a double pointer so we must dereference it once
add_debug_source_node(g, node);
struct_ptr = LLVMBuildLoad(g->builder, struct_ptr, "");
}
} else {
struct_ptr = gen_expr(g, struct_expr_node);
}
assert(LLVMGetTypeKind(LLVMTypeOf(struct_ptr)) == LLVMPointerTypeKind);
assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(struct_ptr))) == LLVMStructTypeKind);
2016-01-11 09:56:50 +08:00
int gen_field_index = node->data.field_access_expr.type_struct_field->gen_index;
assert(gen_field_index >= 0);
*out_type_entry = node->data.field_access_expr.type_struct_field->type_entry;
2015-12-13 13:55:29 +08:00
add_debug_source_node(g, node);
2016-01-11 09:56:50 +08:00
return LLVMBuildStructGEP(g->builder, struct_ptr, gen_field_index, "");
}
static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeSliceExpr);
AstNode *array_ref_node = node->data.slice_expr.array_ref_expr;
TypeTableEntry *array_type = get_expr_type(array_ref_node);
LLVMValueRef tmp_struct_ptr = node->data.slice_expr.resolved_struct_val_expr.ptr;
LLVMValueRef array_ptr = gen_array_base_ptr(g, array_ref_node);
if (array_type->id == TypeTableEntryIdArray) {
LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
LLVMValueRef end_val;
if (node->data.slice_expr.end) {
end_val = gen_expr(g, node->data.slice_expr.end);
} else {
2016-01-19 12:13:14 +08:00
end_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref, array_type->data.array.len, false);
}
add_debug_source_node(g, node);
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
LLVMValueRef indices[] = {
2016-01-19 12:13:14 +08:00
LLVMConstNull(g->builtin_types.entry_isize->type_ref),
start_val,
};
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
LLVMValueRef len_value = LLVMBuildSub(g->builder, end_val, start_val, "");
LLVMBuildStore(g->builder, len_value, len_field_ptr);
return tmp_struct_ptr;
} else if (array_type->id == TypeTableEntryIdPointer) {
LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
LLVMValueRef end_val = gen_expr(g, node->data.slice_expr.end);
add_debug_source_node(g, node);
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
LLVMValueRef len_value = LLVMBuildSub(g->builder, end_val, start_val, "");
LLVMBuildStore(g->builder, len_value, len_field_ptr);
return tmp_struct_ptr;
} else if (array_type->id == TypeTableEntryIdStruct) {
assert(array_type->data.structure.is_unknown_size_array);
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
LLVMValueRef end_val;
if (node->data.slice_expr.end) {
end_val = gen_expr(g, node->data.slice_expr.end);
} else {
add_debug_source_node(g, node);
LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, 1, "");
end_val = LLVMBuildLoad(g->builder, src_len_ptr, "");
}
add_debug_source_node(g, node);
LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, 0, "");
LLVMValueRef src_ptr = LLVMBuildLoad(g->builder, src_ptr_ptr, "");
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, "");
LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
LLVMValueRef len_value = LLVMBuildSub(g->builder, end_val, start_val, "");
LLVMBuildStore(g->builder, len_value, len_field_ptr);
return tmp_struct_ptr;
} else {
zig_unreachable();
}
}
2016-01-18 19:34:26 +08:00
static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) {
2015-12-09 06:31:43 +08:00
assert(node->type == NodeTypeArrayAccessExpr);
LLVMValueRef ptr = gen_array_ptr(g, node);
2016-01-18 19:34:26 +08:00
TypeTableEntry *child_type;
TypeTableEntry *array_type = get_expr_type(node->data.array_access_expr.array_ref_expr);
if (array_type->id == TypeTableEntryIdPointer) {
child_type = array_type->data.pointer.child_type;
} else if (array_type->id == TypeTableEntryIdStruct) {
assert(array_type->data.structure.is_unknown_size_array);
TypeTableEntry *child_ptr_type = array_type->data.structure.fields[0].type_entry;
assert(child_ptr_type->id == TypeTableEntryIdPointer);
child_type = child_ptr_type->data.pointer.child_type;
} else if (array_type->id == TypeTableEntryIdArray) {
child_type = array_type->data.array.child_type;
} else {
zig_unreachable();
}
2016-01-18 19:34:26 +08:00
if (is_lvalue || !ptr || handle_is_ptr(child_type)) {
return ptr;
} else {
add_debug_source_node(g, node);
return LLVMBuildLoad(g->builder, ptr, "");
}
2015-12-09 05:15:34 +08:00
}
static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) {
assert(node->type == NodeTypeFieldAccessExpr);
AstNode *struct_expr = node->data.field_access_expr.struct_expr;
TypeTableEntry *struct_type = get_expr_type(struct_expr);
Buf *name = &node->data.field_access_expr.field_name;
if (struct_type->id == TypeTableEntryIdArray) {
if (buf_eql_str(name, "len")) {
2016-01-19 12:13:14 +08:00
return LLVMConstInt(g->builtin_types.entry_isize->type_ref,
struct_type->data.array.len, false);
} else if (buf_eql_str(name, "ptr")) {
LLVMValueRef array_val = gen_expr(g, node->data.field_access_expr.struct_expr);
LLVMValueRef indices[] = {
2016-01-19 12:13:14 +08:00
LLVMConstNull(g->builtin_types.entry_isize->type_ref),
LLVMConstNull(g->builtin_types.entry_isize->type_ref),
};
add_debug_source_node(g, node);
return LLVMBuildInBoundsGEP(g->builder, array_val, indices, 2, "");
} else {
zig_panic("gen_field_access_expr bad array field");
}
} else if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer &&
struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct))
{
TypeTableEntry *type_entry;
LLVMValueRef ptr = gen_field_ptr(g, node, &type_entry);
if (is_lvalue || handle_is_ptr(type_entry)) {
return ptr;
} else {
add_debug_source_node(g, node);
return LLVMBuildLoad(g->builder, ptr, "");
}
} else if (struct_type->id == TypeTableEntryIdMetaType) {
2016-01-11 16:15:17 +08:00
assert(!is_lvalue);
TypeTableEntry *child_type = get_type_for_type_node(struct_expr);
if (child_type->id == TypeTableEntryIdEnum) {
return gen_enum_value_expr(g, node, child_type, nullptr);
} else {
zig_unreachable();
}
} else {
zig_unreachable();
}
}
static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node,
TypeTableEntry **out_type_entry)
{
LLVMValueRef target_ref;
if (node->type == NodeTypeSymbol) {
2016-01-26 07:37:45 +08:00
VariableTableEntry *var = find_variable(expr_node->block_context,
&node->data.symbol_expr.symbol);
assert(var);
*out_type_entry = var->type;
target_ref = var->value_ref;
} else if (node->type == NodeTypeArrayAccessExpr) {
TypeTableEntry *array_type = get_expr_type(node->data.array_access_expr.array_ref_expr);
if (array_type->id == TypeTableEntryIdArray) {
*out_type_entry = array_type->data.array.child_type;
target_ref = gen_array_ptr(g, node);
} else if (array_type->id == TypeTableEntryIdPointer) {
*out_type_entry = array_type->data.pointer.child_type;
target_ref = gen_array_ptr(g, node);
} else if (array_type->id == TypeTableEntryIdStruct) {
assert(array_type->data.structure.is_unknown_size_array);
*out_type_entry = array_type->data.structure.fields[0].type_entry->data.pointer.child_type;
target_ref = gen_array_ptr(g, node);
} else {
zig_unreachable();
}
} else if (node->type == NodeTypeFieldAccessExpr) {
target_ref = gen_field_ptr(g, node, out_type_entry);
2016-01-05 10:10:52 +08:00
} else if (node->type == NodeTypePrefixOpExpr) {
assert(node->data.prefix_op_expr.prefix_op == PrefixOpDereference);
AstNode *target_expr = node->data.prefix_op_expr.primary_expr;
TypeTableEntry *type_entry = get_expr_type(target_expr);
assert(type_entry->id == TypeTableEntryIdPointer);
*out_type_entry = type_entry->data.pointer.child_type;
return gen_expr(g, target_expr);
} else {
zig_panic("bad assign target");
}
return target_ref;
}
static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypePrefixOpExpr);
assert(node->data.prefix_op_expr.primary_expr);
2015-11-30 04:37:55 +08:00
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
2016-02-01 12:32:05 +08:00
TypeTableEntry *expr_type = get_expr_type(expr_node);
2015-11-30 04:37:55 +08:00
switch (node->data.prefix_op_expr.prefix_op) {
case PrefixOpInvalid:
zig_unreachable();
case PrefixOpNegation:
{
LLVMValueRef expr = gen_expr(g, expr_node);
2016-02-01 12:32:05 +08:00
if (expr_type->id == TypeTableEntryIdInt) {
add_debug_source_node(g, node);
return LLVMBuildNeg(g->builder, expr, "");
} else if (expr_type->id == TypeTableEntryIdFloat) {
add_debug_source_node(g, node);
return LLVMBuildFNeg(g->builder, expr, "");
} else {
zig_unreachable();
}
}
case PrefixOpBoolNot:
2015-11-30 04:37:55 +08:00
{
LLVMValueRef expr = gen_expr(g, expr_node);
2015-11-30 04:37:55 +08:00
LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr));
add_debug_source_node(g, node);
return LLVMBuildICmp(g->builder, LLVMIntEQ, expr, zero, "");
}
case PrefixOpBinNot:
{
LLVMValueRef expr = gen_expr(g, expr_node);
add_debug_source_node(g, node);
return LLVMBuildNot(g->builder, expr, "");
}
case PrefixOpAddressOf:
case PrefixOpConstAddressOf:
{
TypeTableEntry *lvalue_type;
return gen_lvalue(g, node, expr_node, &lvalue_type);
}
2016-01-05 10:10:52 +08:00
case PrefixOpDereference:
{
LLVMValueRef expr = gen_expr(g, expr_node);
2016-02-01 12:32:05 +08:00
if (expr_type->size_in_bits == 0) {
2016-01-28 05:18:20 +08:00
return nullptr;
} else {
add_debug_source_node(g, node);
return LLVMBuildLoad(g->builder, expr, "");
}
2016-01-05 10:10:52 +08:00
}
2016-01-14 09:15:51 +08:00
case PrefixOpMaybe:
{
zig_panic("TODO codegen PrefixOpMaybe");
}
case PrefixOpError:
{
zig_panic("TODO codegen PrefixOpError");
}
case PrefixOpUnwrapError:
{
LLVMValueRef expr_val = gen_expr(g, expr_node);
TypeTableEntry *expr_type = get_expr_type(expr_node);
assert(expr_type->id == TypeTableEntryIdErrorUnion);
TypeTableEntry *child_type = expr_type->data.error.child_type;
// TODO in debug mode, put a panic here if the error is not 0
if (child_type->size_in_bits > 0) {
add_debug_source_node(g, node);
LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");
if (handle_is_ptr(child_type)) {
return child_val_ptr;
} else {
return LLVMBuildLoad(g->builder, child_val_ptr, "");
}
} else {
return nullptr;
}
}
2016-02-01 17:11:46 +08:00
case PrefixOpUnwrapMaybe:
{
LLVMValueRef expr_val = gen_expr(g, expr_node);
// TODO in debug mode, put a panic here if null
return gen_unwrap_maybe(g, expr_node, expr_val);
}
2015-11-30 04:37:55 +08:00
}
2015-11-28 15:40:54 +08:00
zig_unreachable();
}
2016-01-02 15:06:06 +08:00
static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
LLVMValueRef val1, LLVMValueRef val2,
TypeTableEntry *op1_type, TypeTableEntry *op2_type,
2016-01-02 15:06:06 +08:00
BinOpType bin_op)
{
assert(op1_type == op2_type);
2016-01-02 15:06:06 +08:00
switch (bin_op) {
case BinOpTypeBinOr:
case BinOpTypeAssignBitOr:
2016-01-02 15:06:06 +08:00
add_debug_source_node(g, source_node);
return LLVMBuildOr(g->builder, val1, val2, "");
case BinOpTypeBinXor:
case BinOpTypeAssignBitXor:
2016-01-02 15:06:06 +08:00
add_debug_source_node(g, source_node);
return LLVMBuildXor(g->builder, val1, val2, "");
case BinOpTypeBinAnd:
case BinOpTypeAssignBitAnd:
2016-01-02 15:06:06 +08:00
add_debug_source_node(g, source_node);
return LLVMBuildAnd(g->builder, val1, val2, "");
case BinOpTypeBitShiftLeft:
case BinOpTypeAssignBitShiftLeft:
2016-01-02 15:06:06 +08:00
add_debug_source_node(g, source_node);
2015-11-28 15:40:54 +08:00
return LLVMBuildShl(g->builder, val1, val2, "");
case BinOpTypeBitShiftRight:
case BinOpTypeAssignBitShiftRight:
assert(op1_type->id == TypeTableEntryIdInt);
assert(op2_type->id == TypeTableEntryIdInt);
2016-01-02 15:06:06 +08:00
add_debug_source_node(g, source_node);
if (op1_type->data.integral.is_signed) {
return LLVMBuildAShr(g->builder, val1, val2, "");
} else {
return LLVMBuildLShr(g->builder, val1, val2, "");
}
case BinOpTypeAdd:
case BinOpTypeAssignPlus:
2016-01-02 15:06:06 +08:00
add_debug_source_node(g, source_node);
2015-12-09 05:15:34 +08:00
if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFAdd(g->builder, val1, val2, "");
} else {
return LLVMBuildAdd(g->builder, val1, val2, "");
}
case BinOpTypeSub:
case BinOpTypeAssignMinus:
2016-01-02 15:06:06 +08:00
add_debug_source_node(g, source_node);
2015-12-09 05:15:34 +08:00
if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFSub(g->builder, val1, val2, "");
} else {
return LLVMBuildSub(g->builder, val1, val2, "");
}
case BinOpTypeMult:
case BinOpTypeAssignTimes:
2016-01-02 15:06:06 +08:00
add_debug_source_node(g, source_node);
2015-12-09 05:15:34 +08:00
if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFMul(g->builder, val1, val2, "");
} else {
return LLVMBuildMul(g->builder, val1, val2, "");
}
case BinOpTypeDiv:
case BinOpTypeAssignDiv:
2016-01-02 15:06:06 +08:00
add_debug_source_node(g, source_node);
2015-12-09 05:15:34 +08:00
if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFDiv(g->builder, val1, val2, "");
} else {
2015-12-09 05:15:34 +08:00
assert(op1_type->id == TypeTableEntryIdInt);
if (op1_type->data.integral.is_signed) {
return LLVMBuildSDiv(g->builder, val1, val2, "");
} else {
return LLVMBuildUDiv(g->builder, val1, val2, "");
}
}
case BinOpTypeMod:
case BinOpTypeAssignMod:
2016-01-02 15:06:06 +08:00
add_debug_source_node(g, source_node);
2015-12-09 05:15:34 +08:00
if (op1_type->id == TypeTableEntryIdFloat) {
return LLVMBuildFRem(g->builder, val1, val2, "");
} else {
2015-12-09 05:15:34 +08:00
assert(op1_type->id == TypeTableEntryIdInt);
if (op1_type->data.integral.is_signed) {
return LLVMBuildSRem(g->builder, val1, val2, "");
} else {
return LLVMBuildURem(g->builder, val1, val2, "");
}
}
case BinOpTypeBoolOr:
case BinOpTypeBoolAnd:
case BinOpTypeCmpEq:
case BinOpTypeCmpNotEq:
case BinOpTypeCmpLessThan:
case BinOpTypeCmpGreaterThan:
case BinOpTypeCmpLessOrEq:
case BinOpTypeCmpGreaterOrEq:
case BinOpTypeInvalid:
2015-12-08 11:57:45 +08:00
case BinOpTypeAssign:
2015-12-13 10:07:29 +08:00
case BinOpTypeAssignBoolAnd:
case BinOpTypeAssignBoolOr:
case BinOpTypeUnwrapMaybe:
case BinOpTypeStrCat:
2015-11-28 15:40:54 +08:00
zig_unreachable();
}
zig_unreachable();
}
static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1);
LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
TypeTableEntry *op1_type = get_expr_type(node->data.bin_op_expr.op1);
TypeTableEntry *op2_type = get_expr_type(node->data.bin_op_expr.op2);
2016-01-02 15:06:06 +08:00
return gen_arithmetic_bin_op(g, node, val1, val2, op1_type, op2_type, node->data.bin_op_expr.bin_op);
}
2015-11-28 15:40:54 +08:00
static LLVMIntPredicate cmp_op_to_int_predicate(BinOpType cmp_op, bool is_signed) {
2015-11-28 15:40:54 +08:00
switch (cmp_op) {
case BinOpTypeCmpEq:
2015-11-28 15:40:54 +08:00
return LLVMIntEQ;
case BinOpTypeCmpNotEq:
2015-11-28 15:40:54 +08:00
return LLVMIntNE;
case BinOpTypeCmpLessThan:
2015-11-28 15:40:54 +08:00
return is_signed ? LLVMIntSLT : LLVMIntULT;
case BinOpTypeCmpGreaterThan:
2015-11-28 15:40:54 +08:00
return is_signed ? LLVMIntSGT : LLVMIntUGT;
case BinOpTypeCmpLessOrEq:
2015-11-28 15:40:54 +08:00
return is_signed ? LLVMIntSLE : LLVMIntULE;
case BinOpTypeCmpGreaterOrEq:
2015-11-28 15:40:54 +08:00
return is_signed ? LLVMIntSGE : LLVMIntUGE;
default:
zig_unreachable();
2015-11-28 15:40:54 +08:00
}
}
static LLVMRealPredicate cmp_op_to_real_predicate(BinOpType cmp_op) {
switch (cmp_op) {
case BinOpTypeCmpEq:
return LLVMRealOEQ;
case BinOpTypeCmpNotEq:
return LLVMRealONE;
case BinOpTypeCmpLessThan:
return LLVMRealOLT;
case BinOpTypeCmpGreaterThan:
return LLVMRealOGT;
case BinOpTypeCmpLessOrEq:
return LLVMRealOLE;
case BinOpTypeCmpGreaterOrEq:
return LLVMRealOGE;
default:
zig_unreachable();
}
}
2015-11-28 15:40:54 +08:00
static LLVMValueRef gen_cmp_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
2015-11-28 15:40:54 +08:00
LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1);
LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
2015-11-28 15:40:54 +08:00
TypeTableEntry *op1_type = get_expr_type(node->data.bin_op_expr.op1);
TypeTableEntry *op2_type = get_expr_type(node->data.bin_op_expr.op2);
assert(op1_type == op2_type);
2015-11-28 15:40:54 +08:00
add_debug_source_node(g, node);
2015-12-09 05:15:34 +08:00
if (op1_type->id == TypeTableEntryIdFloat) {
LLVMRealPredicate pred = cmp_op_to_real_predicate(node->data.bin_op_expr.bin_op);
return LLVMBuildFCmp(g->builder, pred, val1, val2, "");
2016-01-11 16:15:17 +08:00
} else if (op1_type->id == TypeTableEntryIdInt) {
2015-12-09 05:15:34 +08:00
LLVMIntPredicate pred = cmp_op_to_int_predicate(node->data.bin_op_expr.bin_op,
op1_type->data.integral.is_signed);
return LLVMBuildICmp(g->builder, pred, val1, val2, "");
2016-01-11 16:15:17 +08:00
} else if (op1_type->id == TypeTableEntryIdEnum) {
LLVMIntPredicate pred = cmp_op_to_int_predicate(node->data.bin_op_expr.bin_op, false);
return LLVMBuildICmp(g->builder, pred, val1, val2, "");
} else {
zig_unreachable();
}
2015-11-28 15:40:54 +08:00
}
static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
2015-11-28 15:40:54 +08:00
LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1);
2015-12-13 08:33:45 +08:00
LLVMBasicBlockRef post_val1_block = LLVMGetInsertBlock(g->builder);
2015-11-28 15:40:54 +08:00
// block for when val1 == true
LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndTrue");
2015-11-28 15:40:54 +08:00
// block for when val1 == false (don't even evaluate the second part)
LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndFalse");
2015-11-28 15:40:54 +08:00
add_debug_source_node(g, node);
LLVMBuildCondBr(g->builder, val1, true_block, false_block);
2015-11-28 15:40:54 +08:00
LLVMPositionBuilderAtEnd(g->builder, true_block);
LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
2015-12-13 08:33:45 +08:00
LLVMBasicBlockRef post_val2_block = LLVMGetInsertBlock(g->builder);
2015-11-28 15:40:54 +08:00
add_debug_source_node(g, node);
2015-12-02 12:19:38 +08:00
LLVMBuildBr(g->builder, false_block);
2015-11-28 15:40:54 +08:00
LLVMPositionBuilderAtEnd(g->builder, false_block);
add_debug_source_node(g, node);
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
LLVMValueRef incoming_values[2] = {val1, val2};
2015-12-13 08:33:45 +08:00
LLVMBasicBlockRef incoming_blocks[2] = {post_val1_block, post_val2_block};
2015-11-28 15:40:54 +08:00
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
return phi;
}
static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
assert(expr_node->type == NodeTypeBinOpExpr);
2015-11-28 15:40:54 +08:00
LLVMValueRef val1 = gen_expr(g, expr_node->data.bin_op_expr.op1);
2015-12-13 08:33:45 +08:00
LLVMBasicBlockRef post_val1_block = LLVMGetInsertBlock(g->builder);
2015-12-02 12:19:38 +08:00
2015-11-28 15:40:54 +08:00
// block for when val1 == false
LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrFalse");
2015-11-28 15:40:54 +08:00
// block for when val1 == true (don't even evaluate the second part)
LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrTrue");
2015-11-28 15:40:54 +08:00
add_debug_source_node(g, expr_node);
LLVMBuildCondBr(g->builder, val1, true_block, false_block);
2015-11-28 15:40:54 +08:00
LLVMPositionBuilderAtEnd(g->builder, false_block);
LLVMValueRef val2 = gen_expr(g, expr_node->data.bin_op_expr.op2);
2015-12-13 08:33:45 +08:00
LLVMBasicBlockRef post_val2_block = LLVMGetInsertBlock(g->builder);
2015-11-28 15:40:54 +08:00
add_debug_source_node(g, expr_node);
2015-12-02 12:19:38 +08:00
LLVMBuildBr(g->builder, true_block);
2015-11-28 15:40:54 +08:00
LLVMPositionBuilderAtEnd(g->builder, true_block);
add_debug_source_node(g, expr_node);
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
LLVMValueRef incoming_values[2] = {val1, val2};
2015-12-13 08:33:45 +08:00
LLVMBasicBlockRef incoming_blocks[2] = {post_val1_block, post_val2_block};
2015-11-28 15:40:54 +08:00
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
return phi;
}
2015-12-24 15:00:23 +08:00
static LLVMValueRef gen_struct_memcpy(CodeGen *g, AstNode *source_node, LLVMValueRef src, LLVMValueRef dest,
TypeTableEntry *type_entry)
{
2016-01-18 19:34:26 +08:00
assert(handle_is_ptr(type_entry));
2015-12-24 15:00:23 +08:00
LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
add_debug_source_node(g, source_node);
LLVMValueRef src_ptr = LLVMBuildBitCast(g->builder, src, ptr_u8, "");
LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, dest, ptr_u8, "");
LLVMValueRef params[] = {
dest_ptr, // dest pointer
src_ptr, // source pointer
LLVMConstInt(LLVMIntType(g->pointer_size_bytes * 8), type_entry->size_in_bits / 8, false), // byte count
LLVMConstInt(LLVMInt32Type(), type_entry->align_in_bits / 8, false), // align in bytes
LLVMConstNull(LLVMInt1Type()), // is volatile
};
return LLVMBuildCall(g->builder, g->memcpy_fn_val, params, 5, "");
}
2016-01-02 15:06:06 +08:00
static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op,
LLVMValueRef target_ref, LLVMValueRef value,
TypeTableEntry *op1_type, TypeTableEntry *op2_type)
{
2016-01-18 19:34:26 +08:00
if (handle_is_ptr(op1_type)) {
2016-01-02 15:06:06 +08:00
assert(op1_type == op2_type);
assert(bin_op == BinOpTypeAssign);
return gen_struct_memcpy(g, source_node, value, target_ref, op1_type);
}
if (bin_op != BinOpTypeAssign) {
assert(source_node->type == NodeTypeBinOpExpr);
add_debug_source_node(g, source_node->data.bin_op_expr.op1);
LLVMValueRef left_value = LLVMBuildLoad(g->builder, target_ref, "");
value = gen_arithmetic_bin_op(g, source_node, left_value, value, op1_type, op2_type, bin_op);
}
add_debug_source_node(g, source_node);
return LLVMBuildStore(g->builder, value, target_ref);
}
2015-12-07 14:09:46 +08:00
static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
2015-12-09 06:31:43 +08:00
AstNode *lhs_node = node->data.bin_op_expr.op1;
2015-12-07 14:09:46 +08:00
TypeTableEntry *op1_type;
LLVMValueRef target_ref = gen_lvalue(g, node, lhs_node, &op1_type);
2015-12-07 14:09:46 +08:00
TypeTableEntry *op2_type = get_expr_type(node->data.bin_op_expr.op2);
LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2);
2016-01-16 09:41:19 +08:00
if (op1_type->size_in_bits == 0) {
return nullptr;
}
2016-01-02 15:06:06 +08:00
return gen_assign_raw(g, node, node->data.bin_op_expr.bin_op, target_ref, value, op1_type, op2_type);
2015-12-07 14:09:46 +08:00
}
static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref) {
TypeTableEntry *type_entry = get_expr_type(node);
assert(type_entry->id == TypeTableEntryIdMaybe);
TypeTableEntry *child_type = type_entry->data.maybe.child_type;
if (child_type->id == TypeTableEntryIdPointer ||
child_type->id == TypeTableEntryIdFn)
{
return maybe_struct_ref;
} else {
add_debug_source_node(g, node);
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, "");
if (handle_is_ptr(child_type)) {
return maybe_field_ptr;
} else {
return LLVMBuildLoad(g->builder, maybe_field_ptr, "");
}
}
}
static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
assert(node->data.bin_op_expr.bin_op == BinOpTypeUnwrapMaybe);
AstNode *op1_node = node->data.bin_op_expr.op1;
AstNode *op2_node = node->data.bin_op_expr.op2;
LLVMValueRef maybe_struct_ref = gen_expr(g, op1_node);
TypeTableEntry *maybe_type = get_expr_type(op1_node);
assert(maybe_type->id == TypeTableEntryIdMaybe);
TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
LLVMValueRef cond_value;
if (child_type->id == TypeTableEntryIdPointer ||
child_type->id == TypeTableEntryIdFn)
{
cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, maybe_struct_ref,
LLVMConstNull(child_type->type_ref), "");
} else {
add_debug_source_node(g, node);
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, "");
cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
}
LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNonNull");
LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNull");
LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEnd");
2016-01-19 07:42:45 +08:00
bool null_reachable = get_expr_type(op2_node)->id != TypeTableEntryIdUnreachable;
LLVMBuildCondBr(g->builder, cond_value, non_null_block, null_block);
LLVMPositionBuilderAtEnd(g->builder, non_null_block);
LLVMValueRef non_null_result = gen_unwrap_maybe(g, op1_node, maybe_struct_ref);
add_debug_source_node(g, node);
LLVMBuildBr(g->builder, end_block);
LLVMBasicBlockRef post_non_null_result_block = LLVMGetInsertBlock(g->builder);
LLVMPositionBuilderAtEnd(g->builder, null_block);
LLVMValueRef null_result = gen_expr(g, op2_node);
if (null_reachable) {
add_debug_source_node(g, node);
LLVMBuildBr(g->builder, end_block);
}
LLVMBasicBlockRef post_null_result_block = LLVMGetInsertBlock(g->builder);
LLVMPositionBuilderAtEnd(g->builder, end_block);
if (null_reachable) {
add_debug_source_node(g, node);
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), "");
LLVMValueRef incoming_values[2] = {non_null_result, null_result};
LLVMBasicBlockRef incoming_blocks[2] = {post_non_null_result_block, post_null_result_block};
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
return phi;
} else {
return non_null_result;
}
return nullptr;
}
static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
switch (node->data.bin_op_expr.bin_op) {
case BinOpTypeInvalid:
case BinOpTypeStrCat:
zig_unreachable();
2015-12-13 10:07:29 +08:00
case BinOpTypeAssign:
case BinOpTypeAssignTimes:
case BinOpTypeAssignDiv:
case BinOpTypeAssignMod:
case BinOpTypeAssignPlus:
case BinOpTypeAssignMinus:
case BinOpTypeAssignBitShiftLeft:
case BinOpTypeAssignBitShiftRight:
case BinOpTypeAssignBitAnd:
case BinOpTypeAssignBitXor:
case BinOpTypeAssignBitOr:
case BinOpTypeAssignBoolAnd:
case BinOpTypeAssignBoolOr:
return gen_assign_expr(g, node);
case BinOpTypeBoolOr:
return gen_bool_or_expr(g, node);
case BinOpTypeBoolAnd:
return gen_bool_and_expr(g, node);
case BinOpTypeCmpEq:
case BinOpTypeCmpNotEq:
case BinOpTypeCmpLessThan:
case BinOpTypeCmpGreaterThan:
case BinOpTypeCmpLessOrEq:
case BinOpTypeCmpGreaterOrEq:
return gen_cmp_expr(g, node);
case BinOpTypeUnwrapMaybe:
return gen_unwrap_maybe_expr(g, node);
case BinOpTypeBinOr:
case BinOpTypeBinXor:
case BinOpTypeBinAnd:
case BinOpTypeBitShiftLeft:
case BinOpTypeBitShiftRight:
case BinOpTypeAdd:
case BinOpTypeSub:
case BinOpTypeMult:
case BinOpTypeDiv:
case BinOpTypeMod:
return gen_arithmetic_bin_op_expr(g, node);
}
zig_unreachable();
}
2016-01-26 04:53:40 +08:00
static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeUnwrapErrorExpr);
AstNode *op1 = node->data.unwrap_err_expr.op1;
AstNode *op2 = node->data.unwrap_err_expr.op2;
VariableTableEntry *var = node->data.unwrap_err_expr.var;
LLVMValueRef expr_val = gen_expr(g, op1);
TypeTableEntry *expr_type = get_expr_type(op1);
TypeTableEntry *op2_type = get_expr_type(op2);
assert(expr_type->id == TypeTableEntryIdErrorUnion);
TypeTableEntry *child_type = expr_type->data.error.child_type;
LLVMValueRef err_val;
add_debug_source_node(g, node);
if (handle_is_ptr(expr_type)) {
LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, "");
err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
} else {
err_val = expr_val;
}
LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrOk");
LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError");
LLVMBasicBlockRef end_block;
bool err_reachable = op2_type->id != TypeTableEntryIdUnreachable;
bool have_end_block = err_reachable && (child_type->size_in_bits > 0);
if (have_end_block) {
end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrEnd");
}
LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block);
LLVMPositionBuilderAtEnd(g->builder, err_block);
if (var) {
LLVMBuildStore(g->builder, err_val, var->value_ref);
}
LLVMValueRef err_result = gen_expr(g, op2);
add_debug_source_node(g, node);
if (have_end_block) {
LLVMBuildBr(g->builder, end_block);
} else if (err_reachable) {
LLVMBuildBr(g->builder, ok_block);
}
LLVMPositionBuilderAtEnd(g->builder, ok_block);
if (child_type->size_in_bits == 0) {
return nullptr;
}
LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");
LLVMValueRef child_val;
if (handle_is_ptr(child_type)) {
child_val = child_val_ptr;
} else {
child_val = LLVMBuildLoad(g->builder, child_val_ptr, "");
}
if (!have_end_block) {
return child_val;
}
LLVMBuildBr(g->builder, end_block);
LLVMPositionBuilderAtEnd(g->builder, end_block);
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(err_result), "");
LLVMValueRef incoming_values[2] = {child_val, err_result};
LLVMBasicBlockRef incoming_blocks[2] = {ok_block, err_block};
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
return phi;
}
static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value) {
TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
if (handle_is_ptr(return_type)) {
assert(g->cur_ret_ptr);
gen_assign_raw(g, source_node, BinOpTypeAssign, g->cur_ret_ptr, value, return_type, return_type);
add_debug_source_node(g, source_node);
return LLVMBuildRetVoid(g->builder);
} else {
add_debug_source_node(g, source_node);
return LLVMBuildRet(g->builder, value);
}
}
2015-11-28 15:40:54 +08:00
static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeReturnExpr);
AstNode *param_node = node->data.return_expr.expr;
2016-01-25 05:51:47 +08:00
assert(param_node);
LLVMValueRef value = gen_expr(g, param_node);
TypeTableEntry *value_type = get_expr_type(param_node);
2015-11-28 15:40:54 +08:00
2016-01-25 05:51:47 +08:00
switch (node->data.return_expr.kind) {
case ReturnKindUnconditional:
return gen_return(g, node, value);
case ReturnKindError:
2016-01-25 05:51:47 +08:00
{
assert(value_type->id == TypeTableEntryIdErrorUnion);
TypeTableEntry *child_type = value_type->data.error.child_type;
2016-01-25 05:51:47 +08:00
2016-01-25 11:13:08 +08:00
LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetReturn");
LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetContinue");
2016-01-25 11:13:08 +08:00
add_debug_source_node(g, node);
LLVMValueRef err_val;
if (child_type->size_in_bits > 0) {
2016-01-25 11:13:08 +08:00
LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, value, 0, "");
err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
} else {
err_val = value;
}
LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block);
LLVMPositionBuilderAtEnd(g->builder, return_block);
TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
2016-01-25 11:13:08 +08:00
if (return_type->id == TypeTableEntryIdPureError) {
gen_return(g, node, err_val);
} else if (return_type->id == TypeTableEntryIdErrorUnion) {
if (return_type->data.error.child_type->size_in_bits > 0) {
assert(g->cur_ret_ptr);
2016-01-25 11:13:08 +08:00
add_debug_source_node(g, node);
LLVMValueRef tag_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 0, "");
LLVMBuildStore(g->builder, err_val, tag_ptr);
LLVMBuildRetVoid(g->builder);
} else {
gen_return(g, node, err_val);
}
} else {
2016-01-25 11:13:08 +08:00
zig_unreachable();
}
LLVMPositionBuilderAtEnd(g->builder, continue_block);
if (child_type->size_in_bits > 0) {
add_debug_source_node(g, node);
LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");
if (handle_is_ptr(child_type)) {
return val_ptr;
} else {
return LLVMBuildLoad(g->builder, val_ptr, "");
}
} else {
return nullptr;
}
2016-01-25 05:51:47 +08:00
}
case ReturnKindMaybe:
zig_panic("TODO");
2015-11-28 15:40:54 +08:00
}
}
2015-12-02 12:19:38 +08:00
2016-01-02 15:06:06 +08:00
static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMValueRef cond_value,
AstNode *then_node, AstNode *else_node)
{
TypeTableEntry *then_type = get_expr_type(then_node);
bool use_expr_value = (then_type->id != TypeTableEntryIdUnreachable &&
then_type->id != TypeTableEntryIdVoid);
2015-12-02 12:19:38 +08:00
2016-01-02 15:06:06 +08:00
if (else_node) {
2015-12-02 12:19:38 +08:00
LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
2016-01-05 21:30:49 +08:00
LLVMBasicBlockRef endif_block;
bool then_endif_reachable = get_expr_type(then_node)->id != TypeTableEntryIdUnreachable;
bool else_endif_reachable = get_expr_type(else_node)->id != TypeTableEntryIdUnreachable;
if (then_endif_reachable || else_endif_reachable) {
endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
}
2015-12-02 12:19:38 +08:00
LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
LLVMPositionBuilderAtEnd(g->builder, then_block);
2016-01-02 15:06:06 +08:00
LLVMValueRef then_expr_result = gen_expr(g, then_node);
2016-01-05 21:30:49 +08:00
if (then_endif_reachable) {
2015-12-03 15:47:35 +08:00
LLVMBuildBr(g->builder, endif_block);
2016-01-05 21:30:49 +08:00
}
LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder);
2015-12-02 12:19:38 +08:00
LLVMPositionBuilderAtEnd(g->builder, else_block);
2016-01-02 15:06:06 +08:00
LLVMValueRef else_expr_result = gen_expr(g, else_node);
2016-01-05 21:30:49 +08:00
if (else_endif_reachable) {
2015-12-03 15:47:35 +08:00
LLVMBuildBr(g->builder, endif_block);
2016-01-05 21:30:49 +08:00
}
LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder);
2015-12-02 12:19:38 +08:00
2016-01-05 21:30:49 +08:00
if (then_endif_reachable || else_endif_reachable) {
LLVMPositionBuilderAtEnd(g->builder, endif_block);
if (use_expr_value) {
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block};
2016-01-05 21:30:49 +08:00
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
2015-12-02 12:19:38 +08:00
2016-01-05 21:30:49 +08:00
return phi;
}
2015-12-02 12:19:38 +08:00
}
return nullptr;
}
assert(!use_expr_value || then_type->id == TypeTableEntryIdErrorUnion);
2015-12-02 12:19:38 +08:00
LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block);
LLVMPositionBuilderAtEnd(g->builder, then_block);
2016-01-02 15:06:06 +08:00
gen_expr(g, then_node);
if (get_expr_type(then_node)->id != TypeTableEntryIdUnreachable)
2015-12-03 15:47:35 +08:00
LLVMBuildBr(g->builder, endif_block);
2015-12-02 12:19:38 +08:00
LLVMPositionBuilderAtEnd(g->builder, endif_block);
2016-01-23 17:14:01 +08:00
if (use_expr_value) {
return LLVMConstNull(g->err_tag_type->type_ref);
} else {
return nullptr;
}
2015-12-02 12:19:38 +08:00
}
2016-01-02 15:06:06 +08:00
static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeIfBoolExpr);
assert(node->data.if_bool_expr.condition);
assert(node->data.if_bool_expr.then_block);
2016-01-21 18:02:25 +08:00
ConstExprValue *const_val = &get_resolved_expr(node->data.if_bool_expr.condition)->const_val;
if (const_val->ok) {
if (const_val->data.x_bool) {
return gen_expr(g, node->data.if_bool_expr.then_block);
} else if (node->data.if_bool_expr.else_node) {
return gen_expr(g, node->data.if_bool_expr.else_node);
} else {
return nullptr;
}
} else {
LLVMValueRef cond_value = gen_expr(g, node->data.if_bool_expr.condition);
2016-01-02 15:06:06 +08:00
2016-01-21 18:02:25 +08:00
return gen_if_bool_expr_raw(g, node, cond_value,
node->data.if_bool_expr.then_block,
node->data.if_bool_expr.else_node);
}
2016-01-02 15:06:06 +08:00
}
2015-12-27 06:05:27 +08:00
static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeIfVarExpr);
2016-01-02 15:06:06 +08:00
assert(node->data.if_var_expr.var_decl.expr);
LLVMValueRef init_val;
TypeTableEntry *expr_type;
gen_var_decl_raw(g, node, &node->data.if_var_expr.var_decl, true, &init_val, &expr_type);
2016-01-02 15:06:06 +08:00
// test if value is the maybe state
assert(expr_type->id == TypeTableEntryIdMaybe);
TypeTableEntry *child_type = expr_type->data.maybe.child_type;
LLVMValueRef cond_value;
if (child_type->id == TypeTableEntryIdPointer ||
child_type->id == TypeTableEntryIdFn)
{
cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, init_val, LLVMConstNull(child_type->type_ref), "");
} else {
add_debug_source_node(g, node);
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, init_val, 1, "");
cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
}
2016-01-02 15:06:06 +08:00
LLVMValueRef return_value = gen_if_bool_expr_raw(g, node, cond_value,
node->data.if_var_expr.then_block,
node->data.if_var_expr.else_node);
return return_value;
2015-12-27 06:05:27 +08:00
}
2015-12-02 12:19:38 +08:00
static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
assert(block_node->type == NodeTypeBlock);
LLVMValueRef return_value;
for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
AstNode *statement_node = block_node->data.block.statements.at(i);
return_value = gen_expr(g, statement_node);
}
if (implicit_return_type && implicit_return_type->id != TypeTableEntryIdUnreachable) {
2016-01-26 07:37:45 +08:00
return gen_return(g, block_node, return_value);
} else {
return return_value;
2015-12-02 12:19:38 +08:00
}
}
2015-12-11 18:55:26 +08:00
static int find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok) {
const char *ptr = buf_ptr(&node->data.asm_expr.asm_template) + tok->start + 2;
int len = tok->end - tok->start - 2;
int result = 0;
for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) {
AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
if (buf_eql_mem(&asm_output->asm_symbolic_name, ptr, len)) {
return result;
}
}
for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) {
AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
if (buf_eql_mem(&asm_input->asm_symbolic_name, ptr, len)) {
return result;
}
}
return -1;
}
2015-12-11 06:34:38 +08:00
static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeAsmExpr);
2015-12-11 18:55:26 +08:00
AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
Buf *src_template = &asm_expr->asm_template;
2015-12-11 06:34:38 +08:00
Buf llvm_template = BUF_INIT;
buf_resize(&llvm_template, 0);
2015-12-11 18:55:26 +08:00
for (int token_i = 0; token_i < asm_expr->token_list.length; token_i += 1) {
AsmToken *asm_token = &asm_expr->token_list.at(token_i);
2015-12-11 06:34:38 +08:00
switch (asm_token->id) {
case AsmTokenIdTemplate:
for (int offset = asm_token->start; offset < asm_token->end; offset += 1) {
uint8_t c = *((uint8_t*)(buf_ptr(src_template) + offset));
if (c == '$') {
buf_append_str(&llvm_template, "$$");
} else {
buf_append_char(&llvm_template, c);
}
}
break;
case AsmTokenIdPercent:
buf_append_char(&llvm_template, '%');
break;
2015-12-11 18:55:26 +08:00
case AsmTokenIdVar:
int index = find_asm_index(g, node, asm_token);
assert(index >= 0);
buf_appendf(&llvm_template, "$%d", index);
break;
}
}
Buf constraint_buf = BUF_INIT;
buf_resize(&constraint_buf, 0);
assert(asm_expr->return_count == 0 || asm_expr->return_count == 1);
2015-12-11 18:55:26 +08:00
int total_constraint_count = asm_expr->output_list.length +
asm_expr->input_list.length +
asm_expr->clobber_list.length;
int input_and_output_count = asm_expr->output_list.length +
asm_expr->input_list.length -
asm_expr->return_count;
2015-12-11 18:55:26 +08:00
int total_index = 0;
int param_index = 0;
2015-12-11 18:55:26 +08:00
LLVMTypeRef *param_types = allocate<LLVMTypeRef>(input_and_output_count);
LLVMValueRef *param_values = allocate<LLVMValueRef>(input_and_output_count);
for (int i = 0; i < asm_expr->output_list.length; i += 1, total_index += 1) {
AsmOutput *asm_output = asm_expr->output_list.at(i);
2015-12-15 16:55:52 +08:00
bool is_return = (asm_output->return_type != nullptr);
assert(*buf_ptr(&asm_output->constraint) == '=');
if (is_return) {
buf_appendf(&constraint_buf, "=%s", buf_ptr(&asm_output->constraint) + 1);
2015-12-11 18:55:26 +08:00
} else {
2015-12-15 16:55:52 +08:00
buf_appendf(&constraint_buf, "=*%s", buf_ptr(&asm_output->constraint) + 1);
2015-12-11 18:55:26 +08:00
}
if (total_index + 1 < total_constraint_count) {
buf_append_char(&constraint_buf, ',');
}
if (!is_return) {
2016-01-26 07:37:45 +08:00
VariableTableEntry *variable = find_variable( node->block_context, &asm_output->variable_name);
assert(variable);
param_types[param_index] = LLVMTypeOf(variable->value_ref);
param_values[param_index] = variable->value_ref;
param_index += 1;
}
2015-12-11 18:55:26 +08:00
}
for (int i = 0; i < asm_expr->input_list.length; i += 1, total_index += 1, param_index += 1) {
2015-12-11 18:55:26 +08:00
AsmInput *asm_input = asm_expr->input_list.at(i);
buf_append_buf(&constraint_buf, &asm_input->constraint);
if (total_index + 1 < total_constraint_count) {
buf_append_char(&constraint_buf, ',');
}
TypeTableEntry *expr_type = get_expr_type(asm_input->expr);
param_types[param_index] = expr_type->type_ref;
param_values[param_index] = gen_expr(g, asm_input->expr);
2015-12-11 18:55:26 +08:00
}
for (int i = 0; i < asm_expr->clobber_list.length; i += 1, total_index += 1) {
Buf *clobber_buf = asm_expr->clobber_list.at(i);
buf_appendf(&constraint_buf, "~{%s}", buf_ptr(clobber_buf));
if (total_index + 1 < total_constraint_count) {
buf_append_char(&constraint_buf, ',');
2015-12-11 06:34:38 +08:00
}
}
LLVMTypeRef ret_type;
if (asm_expr->return_count == 0) {
ret_type = LLVMVoidType();
} else {
ret_type = get_expr_type(node)->type_ref;
}
LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, input_and_output_count, false);
2015-12-11 06:34:38 +08:00
2015-12-11 18:55:26 +08:00
bool is_volatile = asm_expr->is_volatile || (asm_expr->output_list.length == 0);
LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template),
buf_ptr(&constraint_buf), is_volatile, false);
2015-12-11 06:34:38 +08:00
add_debug_source_node(g, node);
2015-12-11 18:55:26 +08:00
return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, "");
2015-12-11 06:34:38 +08:00
}
2016-01-14 09:15:51 +08:00
static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeContainerInitExpr);
2015-12-24 15:00:23 +08:00
TypeTableEntry *type_entry = get_expr_type(node);
2016-01-14 09:15:51 +08:00
if (type_entry->id == TypeTableEntryIdStruct) {
assert(node->data.container_init_expr.kind == ContainerInitKindStruct);
2015-12-24 15:00:23 +08:00
2016-01-23 09:05:22 +08:00
int src_field_count = type_entry->data.structure.src_field_count;
assert(src_field_count == node->data.container_init_expr.entries.length);
2015-12-24 15:00:23 +08:00
2016-01-14 09:15:51 +08:00
StructValExprCodeGen *struct_val_expr_node = &node->data.container_init_expr.resolved_struct_val_expr;
LLVMValueRef tmp_struct_ptr = struct_val_expr_node->ptr;
2015-12-24 15:00:23 +08:00
2016-01-23 09:05:22 +08:00
for (int i = 0; i < src_field_count; i += 1) {
2016-01-14 09:15:51 +08:00
AstNode *field_node = node->data.container_init_expr.entries.at(i);
assert(field_node->type == NodeTypeStructValueField);
TypeStructField *type_struct_field = field_node->data.struct_val_field.type_struct_field;
if (type_struct_field->type_entry->id == TypeTableEntryIdVoid) {
continue;
}
assert(buf_eql_buf(type_struct_field->name, &field_node->data.struct_val_field.name));
add_debug_source_node(g, field_node);
LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, type_struct_field->gen_index, "");
2016-01-18 19:34:26 +08:00
AstNode *expr_node = field_node->data.struct_val_field.expr;
LLVMValueRef value = gen_expr(g, expr_node);
gen_assign_raw(g, field_node, BinOpTypeAssign, field_ptr, value,
type_struct_field->type_entry, get_expr_type(expr_node));
2016-01-11 09:56:50 +08:00
}
2015-12-24 15:00:23 +08:00
2016-01-14 09:15:51 +08:00
return tmp_struct_ptr;
} else if (type_entry->id == TypeTableEntryIdUnreachable) {
assert(node->data.container_init_expr.entries.length == 0);
add_debug_source_node(g, node);
return LLVMBuildUnreachable(g->builder);
} else if (type_entry->id == TypeTableEntryIdVoid) {
assert(node->data.container_init_expr.entries.length == 0);
return nullptr;
2016-01-16 11:01:49 +08:00
} else if (type_entry->id == TypeTableEntryIdArray) {
StructValExprCodeGen *struct_val_expr_node = &node->data.container_init_expr.resolved_struct_val_expr;
LLVMValueRef tmp_array_ptr = struct_val_expr_node->ptr;
int field_count = type_entry->data.array.len;
assert(field_count == node->data.container_init_expr.entries.length);
2016-01-18 19:34:26 +08:00
TypeTableEntry *child_type = type_entry->data.array.child_type;
2016-01-16 11:01:49 +08:00
for (int i = 0; i < field_count; i += 1) {
AstNode *field_node = node->data.container_init_expr.entries.at(i);
LLVMValueRef elem_val = gen_expr(g, field_node);
LLVMValueRef indices[] = {
2016-01-19 12:13:14 +08:00
LLVMConstNull(g->builtin_types.entry_isize->type_ref),
LLVMConstInt(g->builtin_types.entry_isize->type_ref, i, false),
2016-01-16 11:01:49 +08:00
};
add_debug_source_node(g, field_node);
LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, tmp_array_ptr, indices, 2, "");
2016-01-18 19:34:26 +08:00
gen_assign_raw(g, field_node, BinOpTypeAssign, elem_ptr, elem_val,
child_type, get_expr_type(field_node));
2016-01-16 11:01:49 +08:00
}
return tmp_array_ptr;
2016-01-14 09:15:51 +08:00
} else {
zig_unreachable();
2015-12-24 15:00:23 +08:00
}
}
2015-12-25 05:37:43 +08:00
static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeWhileExpr);
assert(node->data.while_expr.condition);
assert(node->data.while_expr.body);
bool condition_always_true = node->data.while_expr.condition_always_true;
bool contains_break = node->data.while_expr.contains_break;
2016-01-09 17:16:54 +08:00
if (condition_always_true) {
// generate a forever loop
2015-12-25 05:37:43 +08:00
LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
2016-01-09 17:16:54 +08:00
LLVMBasicBlockRef end_block = nullptr;
if (contains_break) {
end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
}
add_debug_source_node(g, node);
LLVMBuildBr(g->builder, body_block);
LLVMPositionBuilderAtEnd(g->builder, body_block);
2016-01-09 17:16:54 +08:00
g->break_block_stack.append(end_block);
g->continue_block_stack.append(body_block);
gen_expr(g, node->data.while_expr.body);
2016-01-09 17:16:54 +08:00
g->break_block_stack.pop();
g->continue_block_stack.pop();
if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
add_debug_source_node(g, node);
LLVMBuildBr(g->builder, body_block);
}
2016-01-09 17:16:54 +08:00
if (contains_break) {
LLVMPositionBuilderAtEnd(g->builder, end_block);
}
} else {
// generate a normal while loop
LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileCond");
LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
add_debug_source_node(g, node);
LLVMBuildBr(g->builder, cond_block);
LLVMPositionBuilderAtEnd(g->builder, cond_block);
LLVMValueRef cond_val = gen_expr(g, node->data.while_expr.condition);
add_debug_source_node(g, node->data.while_expr.condition);
LLVMBuildCondBr(g->builder, cond_val, body_block, end_block);
LLVMPositionBuilderAtEnd(g->builder, body_block);
g->break_block_stack.append(end_block);
g->continue_block_stack.append(cond_block);
gen_expr(g, node->data.while_expr.body);
g->break_block_stack.pop();
g->continue_block_stack.pop();
if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
add_debug_source_node(g, node);
LLVMBuildBr(g->builder, cond_block);
}
LLVMPositionBuilderAtEnd(g->builder, end_block);
}
2015-12-25 05:37:43 +08:00
return nullptr;
}
static LLVMValueRef gen_for_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeForExpr);
assert(node->data.for_expr.array_expr);
assert(node->data.for_expr.body);
VariableTableEntry *elem_var = node->data.for_expr.elem_var;
assert(elem_var);
TypeTableEntry *array_type = get_expr_type(node->data.for_expr.array_expr);
VariableTableEntry *index_var = node->data.for_expr.index_var;
assert(index_var);
LLVMValueRef index_ptr = index_var->value_ref;
2016-01-19 12:13:14 +08:00
LLVMValueRef one_const = LLVMConstInt(g->builtin_types.entry_isize->type_ref, 1, false);
LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForCond");
LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForBody");
LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForEnd");
2016-01-18 22:04:57 +08:00
LLVMValueRef array_val = gen_array_base_ptr(g, node->data.for_expr.array_expr);
add_debug_source_node(g, node);
LLVMBuildStore(g->builder, LLVMConstNull(index_var->type->type_ref), index_ptr);
LLVMValueRef len_val;
TypeTableEntry *child_type;
if (array_type->id == TypeTableEntryIdArray) {
2016-01-19 12:13:14 +08:00
len_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref,
array_type->data.array.len, false);
child_type = array_type->data.array.child_type;
} else if (array_type->id == TypeTableEntryIdStruct) {
assert(array_type->data.structure.is_unknown_size_array);
TypeTableEntry *child_ptr_type = array_type->data.structure.fields[0].type_entry;
assert(child_ptr_type->id == TypeTableEntryIdPointer);
child_type = child_ptr_type->data.pointer.child_type;
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, array_val, 1, "");
len_val = LLVMBuildLoad(g->builder, len_field_ptr, "");
} else {
zig_unreachable();
}
LLVMBuildBr(g->builder, cond_block);
LLVMPositionBuilderAtEnd(g->builder, cond_block);
LLVMValueRef index_val = LLVMBuildLoad(g->builder, index_ptr, "");
LLVMValueRef cond = LLVMBuildICmp(g->builder, LLVMIntSLT, index_val, len_val, "");
LLVMBuildCondBr(g->builder, cond, body_block, end_block);
LLVMPositionBuilderAtEnd(g->builder, body_block);
LLVMValueRef elem_ptr = gen_array_elem_ptr(g, node, array_val, array_type, index_val);
LLVMValueRef elem_val = handle_is_ptr(child_type) ? elem_ptr : LLVMBuildLoad(g->builder, elem_ptr, "");
gen_assign_raw(g, node, BinOpTypeAssign, elem_var->value_ref, elem_val,
elem_var->type, child_type);
g->break_block_stack.append(end_block);
g->continue_block_stack.append(cond_block);
gen_expr(g, node->data.for_expr.body);
g->break_block_stack.pop();
g->continue_block_stack.pop();
if (get_expr_type(node->data.for_expr.body)->id != TypeTableEntryIdUnreachable) {
add_debug_source_node(g, node);
LLVMValueRef new_index_val = LLVMBuildAdd(g->builder, index_val, one_const, "");
LLVMBuildStore(g->builder, new_index_val, index_ptr);
LLVMBuildBr(g->builder, cond_block);
}
LLVMPositionBuilderAtEnd(g->builder, end_block);
2015-12-25 05:37:43 +08:00
return nullptr;
}
2015-12-25 06:09:35 +08:00
static LLVMValueRef gen_break(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeBreak);
LLVMBasicBlockRef dest_block = g->break_block_stack.last();
2015-12-25 06:09:35 +08:00
add_debug_source_node(g, node);
return LLVMBuildBr(g->builder, dest_block);
2015-12-25 06:09:35 +08:00
}
static LLVMValueRef gen_continue(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeContinue);
LLVMBasicBlockRef dest_block = g->continue_block_stack.last();
add_debug_source_node(g, node);
return LLVMBuildBr(g->builder, dest_block);
}
2016-01-02 15:06:06 +08:00
static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl,
bool unwrap_maybe, LLVMValueRef *init_value, TypeTableEntry **expr_type)
2016-01-02 15:06:06 +08:00
{
2016-01-26 07:37:45 +08:00
VariableTableEntry *variable = var_decl->variable;
2016-01-02 15:06:06 +08:00
assert(variable);
assert(variable->is_ptr);
if (var_decl->expr) {
*init_value = gen_expr(g, var_decl->expr);
*expr_type = get_expr_type(var_decl->expr);
2016-01-02 15:06:06 +08:00
}
2016-01-16 09:41:19 +08:00
if (variable->type->size_in_bits == 0) {
2016-01-02 15:06:06 +08:00
return nullptr;
2016-01-23 14:12:33 +08:00
}
bool have_init_expr = false;
if (var_decl->expr) {
ConstExprValue *const_val = &get_resolved_expr(var_decl->expr)->const_val;
if (!const_val->ok || !const_val->undef) {
have_init_expr = true;
}
}
if (have_init_expr) {
TypeTableEntry *expr_type = get_expr_type(var_decl->expr);
LLVMValueRef value;
if (unwrap_maybe) {
assert(var_decl->expr);
assert(expr_type->id == TypeTableEntryIdMaybe);
value = gen_unwrap_maybe(g, var_decl->expr, *init_value);
2016-01-23 14:12:33 +08:00
expr_type = expr_type->data.maybe.child_type;
} else {
2016-01-23 14:12:33 +08:00
value = *init_value;
}
gen_assign_raw(g, var_decl->expr, BinOpTypeAssign, variable->value_ref,
value, variable->type, expr_type);
} else {
bool ignore_uninit = false;
TypeTableEntry *var_type = get_type_for_type_node(var_decl->type);
if (var_type->id == TypeTableEntryIdStruct &&
var_type->data.structure.is_unknown_size_array)
{
assert(var_decl->type->type == NodeTypeArrayType);
AstNode *size_node = var_decl->type->data.array_type.size;
if (size_node) {
ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val;
if (!const_val->ok) {
TypeTableEntry *ptr_type = var_type->data.structure.fields[0].type_entry;
assert(ptr_type->id == TypeTableEntryIdPointer);
TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
LLVMValueRef size_val = gen_expr(g, size_node);
add_debug_source_node(g, source_node);
LLVMValueRef ptr_val = LLVMBuildArrayAlloca(g->builder, child_type->type_ref,
size_val, "");
// store the freshly allocated pointer in the unknown size array struct
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder,
variable->value_ref, 0, "");
LLVMBuildStore(g->builder, ptr_val, ptr_field_ptr);
// store the size in the len field
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder,
variable->value_ref, 1, "");
LLVMBuildStore(g->builder, size_val, len_field_ptr);
// don't clobber what we just did with debug initialization
ignore_uninit = true;
}
}
2016-01-02 15:06:06 +08:00
}
2016-01-23 14:12:33 +08:00
if (!ignore_uninit && g->build_type != CodeGenBuildTypeRelease) {
// memset uninitialized memory to 0xa
add_debug_source_node(g, source_node);
LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, variable->value_ref, ptr_u8, "");
LLVMValueRef byte_count = LLVMConstInt(LLVMIntType(g->pointer_size_bytes * 8),
variable->type->size_in_bits / 8, false);
LLVMValueRef align_in_bytes = LLVMConstInt(LLVMInt32Type(),
variable->type->align_in_bits / 8, false);
LLVMValueRef params[] = {
dest_ptr,
fill_char,
byte_count,
align_in_bytes,
LLVMConstNull(LLVMInt1Type()), // is volatile
};
2016-01-02 15:06:06 +08:00
2016-01-23 14:12:33 +08:00
LLVMBuildCall(g->builder, g->memset_fn_val, params, 5, "");
}
2016-01-02 15:06:06 +08:00
}
2016-01-23 14:12:33 +08:00
LLVMZigDILocation *debug_loc = LLVMZigGetDebugLoc(source_node->line + 1, source_node->column + 1,
2016-01-26 07:37:45 +08:00
source_node->block_context->di_scope);
2016-01-23 14:12:33 +08:00
LLVMZigInsertDeclareAtEnd(g->dbuilder, variable->value_ref, variable->di_loc_var, debug_loc,
LLVMGetInsertBlock(g->builder));
return nullptr;
2016-01-02 15:06:06 +08:00
}
static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) {
AstNode *init_expr = node->data.variable_declaration.expr;
if (node->data.variable_declaration.is_const && init_expr) {
TypeTableEntry *init_expr_type = get_expr_type(init_expr);
if (init_expr_type->id == TypeTableEntryIdNumLitFloat ||
init_expr_type->id == TypeTableEntryIdNumLitInt)
{
return nullptr;
}
}
2016-01-02 15:06:06 +08:00
LLVMValueRef init_val;
TypeTableEntry *init_val_type;
return gen_var_decl_raw(g, node, &node->data.variable_declaration, false, &init_val, &init_val_type);
2016-01-02 15:06:06 +08:00
}
2016-01-14 09:15:51 +08:00
static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {
2016-01-19 07:42:45 +08:00
assert(node->type == NodeTypeSymbol);
VariableTableEntry *variable = node->data.symbol_expr.variable;
if (variable) {
if (variable->type->size_in_bits == 0) {
return nullptr;
} else if (variable->is_ptr) {
assert(variable->value_ref);
if (handle_is_ptr(variable->type)) {
2016-01-19 07:42:45 +08:00
return variable->value_ref;
} else {
add_debug_source_node(g, node);
return LLVMBuildLoad(g->builder, variable->value_ref, "");
}
2016-01-12 11:33:06 +08:00
} else {
2016-01-19 07:42:45 +08:00
return variable->value_ref;
2016-01-12 11:33:06 +08:00
}
2016-01-04 10:38:36 +08:00
}
2016-01-19 07:42:45 +08:00
FnTableEntry *fn_entry = node->data.symbol_expr.fn_entry;
assert(fn_entry);
return fn_entry->fn_value;
2016-01-04 10:38:36 +08:00
}
2016-01-20 11:29:09 +08:00
static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeSwitchExpr);
2016-01-20 17:12:24 +08:00
LLVMValueRef target_value = gen_expr(g, node->data.switch_expr.expr);
bool end_unreachable = (get_expr_type(node)->id == TypeTableEntryIdUnreachable);
LLVMBasicBlockRef end_block = end_unreachable ?
nullptr : LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchEnd");
LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchElse");
int prong_count = node->data.switch_expr.prongs.length;
add_debug_source_node(g, node);
LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, prong_count);
ZigList<LLVMValueRef> incoming_values = {0};
ZigList<LLVMBasicBlockRef> incoming_blocks = {0};
AstNode *else_prong = nullptr;
for (int prong_i = 0; prong_i < prong_count; prong_i += 1) {
AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
LLVMBasicBlockRef prong_block;
if (prong_node->data.switch_prong.items.length == 0) {
assert(!else_prong);
else_prong = prong_node;
prong_block = else_block;
} else {
prong_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchProng");
for (int item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) {
AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
assert(item_node->type != NodeTypeSwitchRange);
assert(get_resolved_expr(item_node)->const_val.ok);
LLVMValueRef val = gen_expr(g, item_node);
LLVMAddCase(switch_instr, val, prong_block);
}
}
assert(!prong_node->data.switch_prong.var_symbol);
LLVMPositionBuilderAtEnd(g->builder, prong_block);
AstNode *prong_expr = prong_node->data.switch_prong.expr;
LLVMValueRef prong_val = gen_expr(g, prong_expr);
if (get_expr_type(prong_expr)->id != TypeTableEntryIdUnreachable) {
add_debug_source_node(g, prong_expr);
LLVMBuildBr(g->builder, end_block);
incoming_values.append(prong_val);
incoming_blocks.append(prong_block);
}
}
if (!else_prong) {
LLVMPositionBuilderAtEnd(g->builder, else_block);
add_debug_source_node(g, node);
LLVMBuildUnreachable(g->builder);
}
if (end_unreachable) {
return nullptr;
}
LLVMPositionBuilderAtEnd(g->builder, end_block);
add_debug_source_node(g, node);
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(incoming_values.at(0)), "");
2016-01-20 17:12:24 +08:00
LLVMAddIncoming(phi, incoming_values.items, incoming_blocks.items, incoming_values.length);
return phi;
2016-01-20 11:29:09 +08:00
}
2016-01-23 06:31:35 +08:00
static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
Expr *expr = get_resolved_expr(node);
if (expr->const_val.ok) {
2016-01-28 05:18:20 +08:00
if (expr->type_entry->size_in_bits == 0) {
return nullptr;
} else {
assert(expr->const_llvm_val);
return expr->const_llvm_val;
}
}
2015-11-28 15:40:54 +08:00
switch (node->type) {
case NodeTypeBinOpExpr:
return gen_bin_op_expr(g, node);
2016-01-26 04:53:40 +08:00
case NodeTypeUnwrapErrorExpr:
return gen_unwrap_err_expr(g, node);
2015-11-28 15:40:54 +08:00
case NodeTypeReturnExpr:
return gen_return_expr(g, node);
2015-12-03 07:33:06 +08:00
case NodeTypeVariableDeclaration:
2016-01-02 15:06:06 +08:00
return gen_var_decl_expr(g, node);
case NodeTypePrefixOpExpr:
return gen_prefix_op_expr(g, node);
2015-11-30 05:39:11 +08:00
case NodeTypeFnCallExpr:
return gen_fn_call_expr(g, node);
2015-12-09 05:15:34 +08:00
case NodeTypeArrayAccessExpr:
return gen_array_access_expr(g, node, false);
case NodeTypeSliceExpr:
return gen_slice_expr(g, node);
case NodeTypeFieldAccessExpr:
return gen_field_access_expr(g, node, false);
2015-12-27 06:05:27 +08:00
case NodeTypeIfBoolExpr:
return gen_if_bool_expr(g, node);
case NodeTypeIfVarExpr:
return gen_if_var_expr(g, node);
2015-12-25 05:37:43 +08:00
case NodeTypeWhileExpr:
return gen_while_expr(g, node);
case NodeTypeForExpr:
return gen_for_expr(g, node);
2015-12-11 06:34:38 +08:00
case NodeTypeAsmExpr:
return gen_asm_expr(g, node);
case NodeTypeSymbol:
2016-01-14 09:15:51 +08:00
return gen_symbol(g, node);
2015-12-02 12:19:38 +08:00
case NodeTypeBlock:
return gen_block(g, node, nullptr);
2015-12-03 15:47:35 +08:00
case NodeTypeGoto:
add_debug_source_node(g, node);
return LLVMBuildBr(g->builder, node->data.goto_expr.label_entry->basic_block);
2015-12-25 06:09:35 +08:00
case NodeTypeBreak:
return gen_break(g, node);
case NodeTypeContinue:
return gen_continue(g, node);
2015-12-03 15:47:35 +08:00
case NodeTypeLabel:
{
LabelTableEntry *label_entry = node->data.label.label_entry;
2015-12-07 14:09:46 +08:00
assert(label_entry);
LLVMBasicBlockRef basic_block = label_entry->basic_block;
if (label_entry->entered_from_fallthrough) {
add_debug_source_node(g, node);
LLVMBuildBr(g->builder, basic_block);
}
2015-12-03 15:47:35 +08:00
LLVMPositionBuilderAtEnd(g->builder, basic_block);
2015-12-07 14:09:46 +08:00
return nullptr;
2015-12-03 15:47:35 +08:00
}
2016-01-14 09:15:51 +08:00
case NodeTypeContainerInitExpr:
return gen_container_init_expr(g, node);
2016-01-20 11:29:09 +08:00
case NodeTypeSwitchExpr:
return gen_switch_expr(g, node);
case NodeTypeNumberLiteral:
case NodeTypeBoolLiteral:
case NodeTypeStringLiteral:
case NodeTypeCharLiteral:
case NodeTypeNullLiteral:
case NodeTypeUndefinedLiteral:
case NodeTypeErrorType:
case NodeTypeTypeLiteral:
case NodeTypeArrayType:
// caught by constant expression eval codegen
zig_unreachable();
2015-11-28 15:40:54 +08:00
case NodeTypeRoot:
case NodeTypeRootExportDecl:
case NodeTypeFnProto:
case NodeTypeFnDef:
case NodeTypeFnDecl:
case NodeTypeParamDecl:
case NodeTypeDirective:
2016-01-27 07:00:39 +08:00
case NodeTypeImport:
case NodeTypeCImport:
case NodeTypeStructDecl:
case NodeTypeStructField:
2015-12-24 15:00:23 +08:00
case NodeTypeStructValueField:
2016-01-20 11:29:09 +08:00
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeErrorValueDecl:
case NodeTypeTypeDecl:
2015-11-28 15:40:54 +08:00
zig_unreachable();
2015-11-24 15:35:23 +08:00
}
zig_unreachable();
}
2015-12-03 15:47:35 +08:00
static void build_label_blocks(CodeGen *g, AstNode *block_node) {
assert(block_node->type == NodeTypeBlock);
for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
AstNode *label_node = block_node->data.block.statements.at(i);
if (label_node->type != NodeTypeLabel)
continue;
Buf *name = &label_node->data.label.name;
label_node->data.label.label_entry->basic_block = LLVMAppendBasicBlock(
2015-12-03 15:47:35 +08:00
g->cur_fn->fn_value, buf_ptr(name));
}
}
static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val) {
assert(const_val->ok);
2016-01-23 14:12:33 +08:00
if (const_val->undef) {
return LLVMGetUndef(type_entry->type_ref);
2016-01-23 14:12:33 +08:00
}
2016-01-28 05:18:20 +08:00
switch (type_entry->id) {
case TypeTableEntryIdTypeDecl:
return gen_const_val(g, type_entry->data.type_decl.canonical_type, const_val);
2016-01-28 05:18:20 +08:00
case TypeTableEntryIdInt:
return LLVMConstInt(type_entry->type_ref, bignum_to_twos_complement(&const_val->data.x_bignum), false);
case TypeTableEntryIdPureError:
assert(const_val->data.x_err.err);
return LLVMConstInt(g->builtin_types.entry_pure_error->type_ref,
const_val->data.x_err.err->value, false);
case TypeTableEntryIdFloat:
if (const_val->data.x_bignum.kind == BigNumKindFloat) {
return LLVMConstReal(type_entry->type_ref, const_val->data.x_bignum.data.x_float);
} else {
int64_t x = const_val->data.x_bignum.data.x_uint;
if (const_val->data.x_bignum.is_negative) {
x = -x;
}
return LLVMConstReal(type_entry->type_ref, x);
}
2016-01-28 05:18:20 +08:00
case TypeTableEntryIdBool:
if (const_val->data.x_bool) {
return LLVMConstAllOnes(LLVMInt1Type());
} else {
2016-01-28 05:18:20 +08:00
return LLVMConstNull(LLVMInt1Type());
}
2016-01-28 05:18:20 +08:00
case TypeTableEntryIdMaybe:
{
TypeTableEntry *child_type = type_entry->data.maybe.child_type;
if (child_type->id == TypeTableEntryIdPointer ||
child_type->id == TypeTableEntryIdFn)
{
if (const_val->data.x_maybe) {
return gen_const_val(g, child_type, const_val->data.x_maybe);
} else {
return LLVMConstNull(child_type->type_ref);
}
2016-01-28 05:18:20 +08:00
} else {
LLVMValueRef child_val;
LLVMValueRef maybe_val;
if (const_val->data.x_maybe) {
child_val = gen_const_val(g, child_type, const_val->data.x_maybe);
maybe_val = LLVMConstAllOnes(LLVMInt1Type());
} else {
child_val = LLVMConstNull(child_type->type_ref);
maybe_val = LLVMConstNull(LLVMInt1Type());
}
LLVMValueRef fields[] = {
child_val,
maybe_val,
};
return LLVMConstStruct(fields, 2, false);
2016-01-28 05:18:20 +08:00
}
}
case TypeTableEntryIdStruct:
{
LLVMValueRef *fields = allocate<LLVMValueRef>(type_entry->data.structure.gen_field_count);
for (int i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
TypeStructField *type_struct_field = &type_entry->data.structure.fields[i];
if (type_struct_field->gen_index == -1) {
continue;
}
fields[type_struct_field->gen_index] = gen_const_val(g, type_struct_field->type_entry,
const_val->data.x_struct.fields[i]);
}
return LLVMConstNamedStruct(type_entry->type_ref, fields,
type_entry->data.structure.gen_field_count);
}
case TypeTableEntryIdArray:
{
TypeTableEntry *child_type = type_entry->data.array.child_type;
uint64_t len = type_entry->data.array.len;
LLVMValueRef *values = allocate<LLVMValueRef>(len);
for (int i = 0; i < len; i += 1) {
ConstExprValue *field_value = const_val->data.x_array.fields[i];
values[i] = gen_const_val(g, child_type, field_value);
}
return LLVMConstArray(child_type->type_ref, values, len);
}
case TypeTableEntryIdEnum:
{
LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref;
LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, const_val->data.x_enum.tag, false);
if (type_entry->data.enumeration.gen_field_count == 0) {
return tag_value;
} else {
zig_panic("TODO");
}
}
case TypeTableEntryIdFn:
return const_val->data.x_fn->fn_value;
case TypeTableEntryIdPointer:
{
TypeTableEntry *child_type = type_entry->data.pointer.child_type;
int len = const_val->data.x_ptr.len;
LLVMValueRef target_val;
if (len == 1) {
target_val = gen_const_val(g, child_type, const_val->data.x_ptr.ptr[0]);
} else if (len > 1) {
LLVMValueRef *values = allocate<LLVMValueRef>(len);
for (int i = 0; i < len; i += 1) {
values[i] = gen_const_val(g, child_type, const_val->data.x_ptr.ptr[i]);
}
target_val = LLVMConstArray(child_type->type_ref, values, len);
} else {
zig_unreachable();
}
LLVMValueRef global_value = LLVMAddGlobal(g->module, LLVMTypeOf(target_val), "");
LLVMSetInitializer(global_value, target_val);
LLVMSetLinkage(global_value, LLVMPrivateLinkage);
LLVMSetGlobalConstant(global_value, type_entry->data.pointer.is_const);
LLVMSetUnnamedAddr(global_value, true);
if (len > 1) {
return LLVMConstBitCast(global_value, type_entry->type_ref);
} else {
return global_value;
}
}
case TypeTableEntryIdErrorUnion:
{
TypeTableEntry *child_type = type_entry->data.error.child_type;
if (child_type->size_in_bits == 0) {
uint64_t value = const_val->data.x_err.err ? const_val->data.x_err.err->value : 0;
return LLVMConstInt(g->err_tag_type->type_ref, value, false);
} else {
LLVMValueRef err_tag_value;
LLVMValueRef err_payload_value;
if (const_val->data.x_err.err) {
err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err.err->value, false);
err_payload_value = LLVMConstNull(child_type->type_ref);
} else {
err_tag_value = LLVMConstNull(g->err_tag_type->type_ref);
err_payload_value = gen_const_val(g, child_type, const_val->data.x_err.payload);
}
LLVMValueRef fields[] = {
err_tag_value,
err_payload_value,
};
return LLVMConstStruct(fields, 2, false);
}
}
case TypeTableEntryIdInvalid:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdVoid:
zig_unreachable();
}
}
static void gen_const_globals(CodeGen *g) {
for (int i = 0; i < g->global_const_list.length; i += 1) {
Expr *expr = g->global_const_list.at(i);
ConstExprValue *const_val = &expr->const_val;
assert(const_val->ok);
2016-01-23 06:31:35 +08:00
TypeTableEntry *type_entry = expr->type_entry;
2015-12-03 15:47:35 +08:00
if (handle_is_ptr(type_entry)) {
LLVMValueRef init_val = gen_const_val(g, type_entry, const_val);
2016-01-23 09:05:22 +08:00
LLVMValueRef global_value = LLVMAddGlobal(g->module, LLVMTypeOf(init_val), "");
LLVMSetInitializer(global_value, init_val);
2016-01-23 09:05:22 +08:00
LLVMSetLinkage(global_value, LLVMPrivateLinkage);
LLVMSetGlobalConstant(global_value, true);
LLVMSetUnnamedAddr(global_value, true);
expr->const_llvm_val = global_value;
} else {
expr->const_llvm_val = gen_const_val(g, type_entry, const_val);
}
}
2015-12-03 15:47:35 +08:00
}
static void do_code_gen(CodeGen *g) {
assert(!g->errors.length);
gen_const_globals(g);
2015-12-15 14:10:18 +08:00
// Generate module level variables
for (int i = 0; i < g->global_vars.length; i += 1) {
VariableTableEntry *var = g->global_vars.at(i);
if (var->type->id == TypeTableEntryIdNumLitFloat ||
2016-01-28 05:18:20 +08:00
var->type->id == TypeTableEntryIdNumLitInt ||
var->type->size_in_bits == 0)
{
continue;
}
2016-01-23 09:05:22 +08:00
assert(var->decl_node);
assert(var->decl_node->type == NodeTypeVariableDeclaration);
LLVMValueRef global_value;
if (var->decl_node->data.variable_declaration.is_extern) {
global_value = LLVMAddGlobal(g->module, var->type->type_ref, buf_ptr(&var->name));
LLVMSetLinkage(global_value, LLVMExternalLinkage);
} else {
AstNode *expr_node = var->decl_node->data.variable_declaration.expr;
LLVMValueRef init_val;
if (expr_node) {
Expr *expr = get_resolved_expr(expr_node);
ConstExprValue *const_val = &expr->const_val;
assert(const_val->ok);
TypeTableEntry *type_entry = expr->type_entry;
init_val = gen_const_val(g, type_entry, const_val);
} else {
init_val = LLVMConstNull(var->type->type_ref);
}
global_value = LLVMAddGlobal(g->module, LLVMTypeOf(init_val), buf_ptr(&var->name));
LLVMSetInitializer(global_value, init_val);
LLVMSetLinkage(global_value, LLVMInternalLinkage);
LLVMSetUnnamedAddr(global_value, true);
}
2015-12-15 14:10:18 +08:00
LLVMSetGlobalConstant(global_value, var->is_const);
var->value_ref = global_value;
}
// Generate function prototypes
2015-12-15 14:10:18 +08:00
for (int fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) {
FnTableEntry *fn_table_entry = g->fn_protos.at(fn_proto_i);
AstNode *proto_node = fn_table_entry->proto_node;
assert(proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
2015-11-24 15:35:23 +08:00
TypeTableEntry *fn_type = fn_table_entry->type_entry;
if (handle_is_ptr(fn_type->data.fn.fn_type_id.return_type)) {
LLVMValueRef first_arg = LLVMGetParam(fn_table_entry->fn_value, 0);
LLVMAddAttribute(first_arg, LLVMStructRetAttribute);
}
// set parameter attributes
for (int param_decl_i = 0; param_decl_i < fn_proto->params.length; param_decl_i += 1) {
AstNode *param_node = fn_proto->params.at(param_decl_i);
assert(param_node->type == NodeTypeParamDecl);
FnGenParamInfo *info = &fn_type->data.fn.gen_param_info[param_decl_i];
int gen_index = info->gen_index;
bool is_byval = info->is_byval;
if (gen_index < 0) {
continue;
}
AstNode *type_node = param_node->data.param_decl.type;
TypeTableEntry *param_type = fn_proto_type_from_type_node(g, type_node);
LLVMValueRef argument_val = LLVMGetParam(fn_table_entry->fn_value, gen_index);
bool param_is_noalias = param_node->data.param_decl.is_noalias;
if (param_type->id == TypeTableEntryIdPointer && param_is_noalias) {
LLVMAddAttribute(argument_val, LLVMNoAliasAttribute);
}
if (param_type->id == TypeTableEntryIdPointer && param_type->data.pointer.is_const) {
LLVMAddAttribute(argument_val, LLVMReadOnlyAttribute);
}
2016-01-25 10:27:12 +08:00
if (param_type->id == TypeTableEntryIdPointer) {
// when https://github.com/andrewrk/zig/issues/82 is fixed, add
// non null attribute here
}
if (is_byval) {
LLVMAddAttribute(argument_val, LLVMByValAttribute);
}
}
}
// Generate function definitions.
2015-12-13 13:55:29 +08:00
for (int fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) {
FnTableEntry *fn_table_entry = g->fn_defs.at(fn_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;
if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) {
g->cur_ret_ptr = LLVMGetParam(fn, 0);
} else {
g->cur_ret_ptr = nullptr;
}
AstNode *proto_node = fn_table_entry->proto_node;
assert(proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
2015-11-25 10:07:33 +08:00
2015-11-24 17:43:45 +08:00
LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn, "entry");
LLVMPositionBuilderAtEnd(g->builder, entry_block);
2015-11-24 15:35:23 +08:00
AstNode *body_node = fn_def_node->data.fn_def.body;
build_label_blocks(g, body_node);
2015-12-03 15:47:35 +08:00
// Set up debug info for blocks and variables and
2015-12-07 14:09:46 +08:00
// allocate all local variables
for (int bc_i = 0; bc_i < fn_table_entry->all_block_contexts.length; bc_i += 1) {
BlockContext *block_context = fn_table_entry->all_block_contexts.at(bc_i);
2015-12-07 14:09:46 +08:00
2015-12-15 14:10:18 +08:00
if (!block_context->di_scope) {
LLVMZigDILexicalBlock *di_block = LLVMZigCreateLexicalBlock(g->dbuilder,
block_context->parent->di_scope,
import->di_file,
block_context->node->line + 1,
block_context->node->column + 1);
block_context->di_scope = LLVMZigLexicalBlockToScope(di_block);
2015-12-08 11:57:45 +08:00
}
for (int var_i = 0; var_i < block_context->variable_list.length; var_i += 1) {
VariableTableEntry *var = block_context->variable_list.at(var_i);
2015-12-07 14:09:46 +08:00
2016-01-16 09:41:19 +08:00
if (var->type->size_in_bits == 0) {
2015-12-08 11:57:45 +08:00
continue;
2016-01-16 09:41:19 +08:00
}
2015-12-08 11:57:45 +08:00
unsigned tag;
unsigned arg_no;
if (block_context->node->type == NodeTypeFnDef) {
tag = LLVMZigTag_DW_arg_variable();
arg_no = var->gen_arg_index + 1;
var->is_ptr = false;
var->value_ref = LLVMGetParam(fn, var->gen_arg_index);
} else {
tag = LLVMZigTag_DW_auto_variable();
arg_no = 0;
add_debug_source_node(g, var->decl_node);
var->value_ref = LLVMBuildAlloca(g->builder, var->type->type_ref, buf_ptr(&var->name));
LLVMSetAlignment(var->value_ref, var->type->align_in_bits / 8);
}
var->di_loc_var = LLVMZigCreateLocalVariable(g->dbuilder, tag,
block_context->di_scope, buf_ptr(&var->name),
import->di_file, var->decl_node->line + 1,
var->type->di_type, !g->strip_debug_symbols, 0, arg_no);
2015-12-07 14:09:46 +08:00
}
2015-12-13 13:55:29 +08:00
// allocate structs which are the result of casts
2016-01-23 06:31:35 +08:00
for (int cea_i = 0; cea_i < block_context->cast_alloca_list.length; cea_i += 1) {
AstNode *fn_call_node = block_context->cast_alloca_list.at(cea_i);
add_debug_source_node(g, fn_call_node);
Expr *expr = &fn_call_node->data.fn_call_expr.resolved_expr;
fn_call_node->data.fn_call_expr.tmp_ptr = LLVMBuildAlloca(g->builder,
expr->type_entry->type_ref, "");
2015-12-13 13:55:29 +08:00
}
2015-12-24 15:00:23 +08:00
// allocate structs which are struct value expressions
for (int alloca_i = 0; alloca_i < block_context->struct_val_expr_alloca_list.length; alloca_i += 1) {
StructValExprCodeGen *struct_val_expr_node = block_context->struct_val_expr_alloca_list.at(alloca_i);
2015-12-24 15:00:23 +08:00
add_debug_source_node(g, struct_val_expr_node->source_node);
struct_val_expr_node->ptr = LLVMBuildAlloca(g->builder,
struct_val_expr_node->type_entry->type_ref, "");
}
2015-12-07 14:09:46 +08:00
}
// create debug variable declarations for parameters
for (int param_i = 0; param_i < fn_proto->params.length; param_i += 1) {
AstNode *param_decl = fn_proto->params.at(param_i);
assert(param_decl->type == NodeTypeParamDecl);
FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i];
if (info->gen_index < 0) {
continue;
}
VariableTableEntry *variable = param_decl->data.param_decl.variable;
assert(variable);
LLVMZigDILocation *debug_loc = LLVMZigGetDebugLoc(param_decl->line + 1, param_decl->column + 1,
fn_def_node->data.fn_def.block_context->di_scope);
LLVMZigInsertDeclareAtEnd(g->dbuilder, variable->value_ref, variable->di_loc_var, debug_loc,
entry_block);
}
TypeTableEntry *implicit_return_type = fn_def_node->data.fn_def.implicit_return_type;
2015-12-02 12:19:38 +08:00
gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type);
2015-11-24 15:35:23 +08:00
}
assert(!g->errors.length);
LLVMZigDIBuilderFinalize(g->dbuilder);
2015-11-25 10:07:33 +08:00
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.
#ifndef NDEBUG
char *error = nullptr;
LLVMVerifyModule(g->module, LLVMAbortProcessAction, &error);
#endif
}
static const int int_sizes_in_bits[] = {
8,
16,
32,
64,
};
2016-01-26 14:21:13 +08:00
struct CIntTypeInfo {
CIntType id;
const char *name;
bool is_signed;
};
static const CIntTypeInfo c_int_type_infos[] = {
{CIntTypeShort, "c_short", true},
{CIntTypeUShort, "c_ushort", false},
{CIntTypeInt, "c_int", true},
{CIntTypeUInt, "c_uint", false},
{CIntTypeLong, "c_long", true},
{CIntTypeULong, "c_ulong", false},
{CIntTypeLongLong, "c_longlong", true},
{CIntTypeULongLong, "c_ulonglong", false},
};
static int get_c_type_size_in_bits(CodeGen *g, CIntType id) {
// TODO other architectures besides x86_64
switch (id) {
case CIntTypeShort:
case CIntTypeUShort:
return 16;
case CIntTypeInt:
case CIntTypeUInt:
return 32;
case CIntTypeLong:
case CIntTypeULong:
case CIntTypeLongLong:
case CIntTypeULongLong:
return 64;
}
zig_unreachable();
}
static void define_builtin_types(CodeGen *g) {
2015-12-01 13:12:21 +08:00
{
// if this type is anywhere in the AST, we should never hit codegen.
2015-12-09 05:15:34 +08:00
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInvalid);
2015-12-01 13:12:21 +08:00
buf_init_from_str(&entry->name, "(invalid)");
g->builtin_types.entry_invalid = entry;
}
2016-01-21 18:02:25 +08:00
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitFloat);
buf_init_from_str(&entry->name, "(float literal)");
g->builtin_types.entry_num_lit_float = entry;
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitInt);
buf_init_from_str(&entry->name, "(integer literal)");
g->builtin_types.entry_num_lit_int = entry;
}
2016-01-23 14:12:33 +08:00
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUndefLit);
buf_init_from_str(&entry->name, "(undefined)");
g->builtin_types.entry_undef = entry;
}
for (int i = 0; i < array_length(int_sizes_in_bits); i += 1) {
int size_in_bits = int_sizes_in_bits[i];
bool is_signed = true;
for (;;) {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMIntType(size_in_bits);
const char u_or_i = is_signed ? 'i' : 'u';
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "%c%d", u_or_i, size_in_bits);
entry->size_in_bits = size_in_bits;
entry->align_in_bits = size_in_bits;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
is_signed ? LLVMZigEncoding_DW_ATE_signed() : LLVMZigEncoding_DW_ATE_unsigned());
entry->data.integral.is_signed = is_signed;
g->primitive_type_table.put(&entry->name, entry);
get_int_type_ptr(g, is_signed, size_in_bits)[0] = entry;
if (!is_signed) {
break;
} else {
is_signed = false;
}
}
}
2016-01-26 14:21:13 +08:00
for (int i = 0; i < array_length(c_int_type_infos); i += 1) {
const CIntTypeInfo *info = &c_int_type_infos[i];
uint64_t size_in_bits = get_c_type_size_in_bits(g, info->id);
bool is_signed = info->is_signed;
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMIntType(size_in_bits);
buf_init_from_str(&entry->name, info->name);
entry->size_in_bits = size_in_bits;
entry->align_in_bits = size_in_bits;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
is_signed ? LLVMZigEncoding_DW_ATE_signed() : LLVMZigEncoding_DW_ATE_unsigned());
entry->data.integral.is_signed = is_signed;
g->primitive_type_table.put(&entry->name, entry);
get_c_int_type_ptr(g, info->id)[0] = entry;
2016-01-26 14:21:13 +08:00
}
2015-12-02 12:19:38 +08:00
{
2015-12-09 05:15:34 +08:00
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool);
2015-12-02 12:19:38 +08:00
entry->type_ref = LLVMInt1Type();
buf_init_from_str(&entry->name, "bool");
entry->size_in_bits = 8;
2015-12-08 11:07:46 +08:00
entry->align_in_bits = 8;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
2015-12-02 12:19:38 +08:00
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_bool = entry;
g->primitive_type_table.put(&entry->name, entry);
2015-12-02 12:19:38 +08:00
}
2015-12-11 06:34:38 +08:00
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
buf_init_from_str(&entry->name, "isize");
entry->size_in_bits = g->pointer_size_bytes * 8;
entry->align_in_bits = g->pointer_size_bytes * 8;
entry->data.integral.is_signed = true;
2015-12-11 06:34:38 +08:00
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
g->builtin_types.entry_isize = entry;
g->primitive_type_table.put(&entry->name, entry);
2015-12-11 06:34:38 +08:00
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
buf_init_from_str(&entry->name, "usize");
entry->size_in_bits = g->pointer_size_bytes * 8;
entry->align_in_bits = g->pointer_size_bytes * 8;
entry->data.integral.is_signed = false;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_usize = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
2015-12-09 05:15:34 +08:00
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
entry->type_ref = LLVMFloatType();
buf_init_from_str(&entry->name, "f32");
entry->size_in_bits = 32;
entry->align_in_bits = 32;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_float());
g->builtin_types.entry_f32 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
entry->type_ref = LLVMDoubleType();
buf_init_from_str(&entry->name, "f64");
entry->size_in_bits = 64;
entry->align_in_bits = 64;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_float());
g->builtin_types.entry_f64 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
entry->type_ref = LLVMX86FP80Type();
buf_init_from_str(&entry->name, "c_long_double");
entry->size_in_bits = 128;
entry->align_in_bits = 128;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
80, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_float());
g->builtin_types.entry_c_long_double = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
2015-12-09 05:15:34 +08:00
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid);
entry->type_ref = LLVMVoidType();
buf_init_from_str(&entry->name, "void");
2015-12-08 11:07:46 +08:00
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_void = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
2015-12-09 05:15:34 +08:00
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUnreachable);
entry->type_ref = LLVMVoidType();
buf_init_from_str(&entry->name, "unreachable");
2015-12-01 13:12:21 +08:00
entry->di_type = g->builtin_types.entry_void->di_type;
g->builtin_types.entry_unreachable = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMetaType);
buf_init_from_str(&entry->name, "type");
g->builtin_types.entry_type = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
// partially complete the error type. we complete it later after we know
// error_value_count.
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError);
buf_init_from_str(&entry->name, "error");
g->builtin_types.entry_pure_error = entry;
g->primitive_type_table.put(&entry->name, entry);
}
g->builtin_types.entry_u8 = get_int_type(g, false, 8);
g->builtin_types.entry_u16 = get_int_type(g, false, 16);
g->builtin_types.entry_u32 = get_int_type(g, false, 32);
g->builtin_types.entry_u64 = get_int_type(g, false, 64);
g->builtin_types.entry_i8 = get_int_type(g, true, 8);
g->builtin_types.entry_i16 = get_int_type(g, true, 16);
g->builtin_types.entry_i32 = get_int_type(g, true, 32);
g->builtin_types.entry_i64 = get_int_type(g, true, 64);
}
2016-01-14 09:15:51 +08:00
static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name) {
BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1);
buf_init_from_str(&builtin_fn->name, name);
builtin_fn->id = id;
g->builtin_fn_table.put(&builtin_fn->name, builtin_fn);
return builtin_fn;
}
static BuiltinFnEntry *create_builtin_fn_with_arg_count(CodeGen *g, BuiltinFnId id, const char *name, int count) {
2016-01-14 09:15:51 +08:00
BuiltinFnEntry *builtin_fn = create_builtin_fn(g, id, name);
builtin_fn->param_count = count;
builtin_fn->param_types = allocate<TypeTableEntry *>(count);
2016-01-14 09:15:51 +08:00
return builtin_fn;
}
static void define_builtin_fns(CodeGen *g) {
2016-01-09 17:16:54 +08:00
{
2016-01-14 09:15:51 +08:00
BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy");
2016-01-09 17:16:54 +08:00
builtin_fn->return_type = g->builtin_types.entry_void;
builtin_fn->param_count = 3;
builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
builtin_fn->param_types[0] = nullptr; // manually checked later
builtin_fn->param_types[1] = nullptr; // manually checked later
2016-01-19 12:13:14 +08:00
builtin_fn->param_types[2] = g->builtin_types.entry_isize;
2016-01-09 17:16:54 +08:00
LLVMTypeRef param_types[] = {
LLVMPointerType(LLVMInt8Type(), 0),
LLVMPointerType(LLVMInt8Type(), 0),
LLVMIntType(g->pointer_size_bytes * 8),
LLVMInt32Type(),
LLVMInt1Type(),
};
LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), param_types, 5, false);
Buf *name = buf_sprintf("llvm.memcpy.p0i8.p0i8.i%d", g->pointer_size_bytes * 8);
2016-01-12 11:33:06 +08:00
builtin_fn->fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type);
assert(LLVMGetIntrinsicID(builtin_fn->fn_val));
2016-01-09 17:16:54 +08:00
2016-01-12 11:33:06 +08:00
g->memcpy_fn_val = builtin_fn->fn_val;
2016-01-09 17:16:54 +08:00
}
{
2016-01-14 09:15:51 +08:00
BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdMemset, "memset");
2016-01-09 17:16:54 +08:00
builtin_fn->return_type = g->builtin_types.entry_void;
builtin_fn->param_count = 3;
builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
builtin_fn->param_types[0] = nullptr; // manually checked later
builtin_fn->param_types[1] = g->builtin_types.entry_u8;
2016-01-19 12:13:14 +08:00
builtin_fn->param_types[2] = g->builtin_types.entry_isize;
2016-01-09 17:16:54 +08:00
LLVMTypeRef param_types[] = {
LLVMPointerType(LLVMInt8Type(), 0),
LLVMInt8Type(),
LLVMIntType(g->pointer_size_bytes * 8),
LLVMInt32Type(),
LLVMInt1Type(),
};
LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), param_types, 5, false);
Buf *name = buf_sprintf("llvm.memset.p0i8.i%d", g->pointer_size_bytes * 8);
builtin_fn->fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type);
assert(LLVMGetIntrinsicID(builtin_fn->fn_val));
2016-01-12 11:33:06 +08:00
g->memset_fn_val = builtin_fn->fn_val;
2016-01-09 17:16:54 +08:00
}
create_builtin_fn_with_arg_count(g, BuiltinFnIdSizeof, "sizeof", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdMaxValue, "max_value", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdMinValue, "min_value", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdMemberCount, "member_count", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdTypeof, "typeof", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdAddWithOverflow, "add_with_overflow", 4);
create_builtin_fn_with_arg_count(g, BuiltinFnIdSubWithOverflow, "sub_with_overflow", 4);
create_builtin_fn_with_arg_count(g, BuiltinFnIdMulWithOverflow, "mul_with_overflow", 4);
2016-01-27 07:00:39 +08:00
create_builtin_fn_with_arg_count(g, BuiltinFnIdCInclude, "c_include", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdCDefine, "c_define", 2);
create_builtin_fn_with_arg_count(g, BuiltinFnIdCUndef, "c_undef", 1);
}
static void init(CodeGen *g, Buf *source_path) {
g->lib_search_paths.append(g->root_source_dir);
g->lib_search_paths.append(buf_create_from_str(ZIG_STD_DIR));
LLVMInitializeAllTargets();
LLVMInitializeAllTargetMCs();
LLVMInitializeAllAsmPrinters();
LLVMInitializeAllAsmParsers();
LLVMInitializeNativeTarget();
g->is_native_target = true;
char *native_triple = LLVMGetDefaultTargetTriple();
g->module = LLVMModuleCreateWithName(buf_ptr(source_path));
LLVMSetTarget(g->module, native_triple);
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);
char *layout_str = LLVMCopyStringRepOfTargetData(g->target_data_ref);
LLVMSetDataLayout(g->module, layout_str);
g->pointer_size_bytes = LLVMPointerSize(g->target_data_ref);
g->builder = LLVMCreateBuilder();
g->dbuilder = LLVMZigCreateDIBuilder(g->module, true);
LLVMZigSetFastMath(g->builder, true);
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);
// This is for debug stuff that doesn't have a real file.
g->dummy_di_file = nullptr;
define_builtin_types(g);
define_builtin_fns(g);
}
void codegen_parseh(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source_code) {
find_libc_path(g);
Buf *full_path = buf_alloc();
os_path_join(src_dirname, src_basename, full_path);
ImportTableEntry *import = allocate<ImportTableEntry>(1);
import->source_code = source_code;
import->path = full_path;
import->fn_table.init(32);
g->root_import = import;
init(g, full_path);
import->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname));
ZigList<ErrorMsg *> errors = {0};
int err = parse_h_buf(import, &errors, source_code, g, nullptr);
if (err) {
fprintf(stderr, "unable to parse .h file: %s\n", err_str(err));
exit(1);
}
if (errors.length > 0) {
for (int i = 0; i < errors.length; i += 1) {
ErrorMsg *err_msg = errors.at(i);
print_err_msg(err_msg, g->err_color);
}
exit(1);
}
}
void codegen_render_ast(CodeGen *g, FILE *f, int indent_size) {
ast_render(stdout, g->root_import->root, 4);
}
static int parse_version_string(Buf *buf, int *major, int *minor, int *patch) {
char *dot1 = strstr(buf_ptr(buf), ".");
if (!dot1)
return ErrorInvalidFormat;
char *dot2 = strstr(dot1 + 1, ".");
if (!dot2)
return ErrorInvalidFormat;
*major = (int)strtol(buf_ptr(buf), nullptr, 10);
*minor = (int)strtol(dot1 + 1, nullptr, 10);
*patch = (int)strtol(dot2 + 1, nullptr, 10);
return ErrorNone;
}
static void set_root_export_version(CodeGen *g, Buf *version_buf, AstNode *node) {
int err;
if ((err = parse_version_string(version_buf, &g->version_major, &g->version_minor, &g->version_patch))) {
add_node_error(g, node,
buf_sprintf("invalid version string"));
}
}
static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path,
Buf *src_dirname, Buf *src_basename, Buf *source_code)
{
2015-12-01 17:29:21 +08:00
int err;
Buf *full_path = buf_alloc();
os_path_join(src_dirname, src_basename, full_path);
if (g->verbose) {
fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(full_path));
fprintf(stderr, "----------------\n");
fprintf(stderr, "%s\n", buf_ptr(source_code));
fprintf(stderr, "\nTokens:\n");
fprintf(stderr, "---------\n");
}
Tokenization tokenization = {0};
tokenize(source_code, &tokenization);
if (tokenization.err) {
ErrorMsg *err = err_msg_create_with_line(full_path, tokenization.err_line, tokenization.err_column,
source_code, tokenization.line_offsets, tokenization.err);
print_err_msg(err, g->err_color);
exit(1);
}
if (g->verbose) {
print_tokens(source_code, tokenization.tokens);
fprintf(stderr, "\nAST:\n");
fprintf(stderr, "------\n");
}
ImportTableEntry *import_entry = allocate<ImportTableEntry>(1);
import_entry->source_code = source_code;
import_entry->line_offsets = tokenization.line_offsets;
import_entry->path = full_path;
2015-12-01 13:53:37 +08:00
import_entry->fn_table.init(32);
2016-01-04 18:52:37 +08:00
import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color,
&g->next_node_index);
assert(import_entry->root);
if (g->verbose) {
ast_print(stderr, import_entry->root, 0);
}
import_entry->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname));
g->import_table.put(abs_full_path, import_entry);
2015-12-15 14:10:18 +08:00
import_entry->block_context = new_block_context(import_entry->root, nullptr);
import_entry->block_context->di_scope = LLVMZigFileToScope(import_entry->di_file);
2015-12-01 13:53:37 +08:00
assert(import_entry->root->type == NodeTypeRoot);
for (int decl_i = 0; decl_i < import_entry->root->data.root.top_level_decls.length; decl_i += 1) {
AstNode *top_level_decl = import_entry->root->data.root.top_level_decls.at(decl_i);
if (top_level_decl->type == NodeTypeRootExportDecl) {
if (g->root_import) {
add_node_error(g, top_level_decl,
buf_sprintf("root export declaration only valid in root source file"));
} else {
for (int i = 0; i < top_level_decl->data.root_export_decl.directives->length; i += 1) {
AstNode *directive_node = top_level_decl->data.root_export_decl.directives->at(i);
Buf *name = &directive_node->data.directive.name;
Buf *param = &directive_node->data.directive.param;
if (buf_eql_str(name, "version")) {
set_root_export_version(g, param, directive_node);
} else if (buf_eql_str(name, "link")) {
g->link_table.put(param, true);
if (buf_eql_str(param, "c")) {
g->link_libc = true;
}
} else {
add_node_error(g, directive_node,
buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
}
}
if (g->root_export_decl) {
add_node_error(g, top_level_decl,
buf_sprintf("only one root export declaration allowed"));
} else {
g->root_export_decl = top_level_decl;
if (!g->root_out_name)
g->root_out_name = &top_level_decl->data.root_export_decl.name;
Buf *out_type = &top_level_decl->data.root_export_decl.type;
OutType export_out_type;
if (buf_eql_str(out_type, "executable")) {
export_out_type = OutTypeExe;
} else if (buf_eql_str(out_type, "library")) {
export_out_type = OutTypeLib;
} else if (buf_eql_str(out_type, "object")) {
export_out_type = OutTypeObj;
} else {
add_node_error(g, top_level_decl,
buf_sprintf("invalid export type: '%s'", buf_ptr(out_type)));
}
if (g->out_type == OutTypeUnknown) {
g->out_type = export_out_type;
}
}
}
2016-01-27 07:00:39 +08:00
} else if (top_level_decl->type == NodeTypeImport) {
Buf *import_target_path = &top_level_decl->data.import.path;
Buf full_path = BUF_INIT;
Buf *import_code = buf_alloc();
bool found_it = false;
for (int path_i = 0; path_i < g->lib_search_paths.length; path_i += 1) {
Buf *search_path = g->lib_search_paths.at(path_i);
os_path_join(search_path, import_target_path, &full_path);
Buf *abs_full_path = buf_alloc();
if ((err = os_path_real(&full_path, abs_full_path))) {
if (err == ErrorFileNotFound) {
continue;
} else {
2016-01-04 18:31:57 +08:00
g->error_during_imports = true;
add_node_error(g, top_level_decl,
buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
goto done_looking_at_imports;
}
}
auto entry = g->import_table.maybe_get(abs_full_path);
if (entry) {
found_it = true;
2016-01-27 07:00:39 +08:00
top_level_decl->data.import.import = entry->value;
} else {
if ((err = os_fetch_file_path(abs_full_path, import_code))) {
if (err == ErrorFileNotFound) {
continue;
} else {
2016-01-04 18:31:57 +08:00
g->error_during_imports = true;
add_node_error(g, top_level_decl,
buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
goto done_looking_at_imports;
}
}
2016-01-27 07:00:39 +08:00
top_level_decl->data.import.import = codegen_add_code(g,
abs_full_path, search_path, &top_level_decl->data.import.path, import_code);
found_it = true;
}
break;
}
if (!found_it) {
2016-01-04 18:31:57 +08:00
g->error_during_imports = true;
add_node_error(g, top_level_decl,
buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
2015-12-11 06:34:38 +08:00
}
} else if (top_level_decl->type == NodeTypeFnDef) {
AstNode *proto_node = top_level_decl->data.fn_def.fn_proto;
assert(proto_node->type == NodeTypeFnProto);
Buf *proto_name = &proto_node->data.fn_proto.name;
bool is_private = (proto_node->data.fn_proto.visib_mod == VisibModPrivate);
2015-12-11 06:34:38 +08:00
if (buf_eql_str(proto_name, "main") && !is_private) {
g->have_exported_main = true;
2015-12-01 17:29:21 +08:00
}
2015-12-01 13:53:37 +08:00
}
}
done_looking_at_imports:
return import_entry;
2015-12-01 13:53:37 +08:00
}
static ImportTableEntry *add_special_code(CodeGen *g, const char *basename) {
Buf *std_dir = buf_create_from_str(ZIG_STD_DIR);
Buf *code_basename = buf_create_from_str(basename);
Buf path_to_code_src = BUF_INIT;
os_path_join(std_dir, code_basename, &path_to_code_src);
Buf *abs_full_path = buf_alloc();
int err;
if ((err = os_path_real(&path_to_code_src, abs_full_path))) {
zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err));
}
Buf *import_code = buf_alloc();
if ((err = os_fetch_file_path(abs_full_path, import_code))) {
zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err));
}
return codegen_add_code(g, abs_full_path, std_dir, code_basename, import_code);
}
void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *source_code) {
Buf source_path = BUF_INIT;
os_path_join(src_dir, src_basename, &source_path);
init(g, &source_path);
2015-12-01 13:53:37 +08:00
Buf *abs_full_path = buf_alloc();
int err;
if ((err = os_path_real(&source_path, abs_full_path))) {
zig_panic("unable to open '%s': %s", buf_ptr(&source_path), err_str(err));
}
g->root_import = codegen_add_code(g, abs_full_path, src_dir, src_basename, source_code);
2015-12-01 13:53:37 +08:00
if (!g->root_out_name) {
add_node_error(g, g->root_import->root,
buf_sprintf("missing export declaration and output name not provided"));
} else if (g->out_type == OutTypeUnknown) {
add_node_error(g, g->root_import->root,
buf_sprintf("missing export declaration and export type not provided"));
}
if (!g->link_libc) {
if (g->have_exported_main && (g->out_type == OutTypeObj || g->out_type == OutTypeExe)) {
g->bootstrap_import = add_special_code(g, "bootstrap.zig");
2015-12-11 06:34:38 +08:00
}
2016-01-16 09:41:19 +08:00
if (g->out_type == OutTypeExe) {
add_special_code(g, "builtin.zig");
}
2015-12-11 06:34:38 +08:00
}
2015-12-01 13:53:37 +08:00
if (g->verbose) {
fprintf(stderr, "\nSemantic Analysis:\n");
fprintf(stderr, "--------------------\n");
}
2016-01-04 18:31:57 +08:00
if (!g->error_during_imports) {
semantic_analyze(g);
}
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);
print_err_msg(err, g->err_color);
}
exit(1);
}
if (g->verbose) {
fprintf(stderr, "\nCode Generation:\n");
fprintf(stderr, "------------------\n");
}
do_code_gen(g);
}
2015-12-04 08:30:44 +08:00
static void to_c_type(CodeGen *g, AstNode *type_node, Buf *out_buf) {
2016-01-14 09:15:51 +08:00
zig_panic("TODO this function needs some love");
TypeTableEntry *type_entry = get_resolved_expr(type_node)->type_entry;
assert(type_entry);
2015-12-01 06:36:58 +08:00
if (type_entry == g->builtin_types.entry_u8) {
g->c_stdint_used = true;
2015-12-04 08:30:44 +08:00
buf_init_from_str(out_buf, "uint8_t");
2015-12-01 06:36:58 +08:00
} else if (type_entry == g->builtin_types.entry_i32) {
g->c_stdint_used = true;
2015-12-04 08:30:44 +08:00
buf_init_from_str(out_buf, "int32_t");
2015-12-11 06:34:38 +08:00
} else if (type_entry == g->builtin_types.entry_isize) {
g->c_stdint_used = true;
buf_init_from_str(out_buf, "intptr_t");
} else if (type_entry == g->builtin_types.entry_f32) {
buf_init_from_str(out_buf, "float");
2015-12-04 08:06:04 +08:00
} else if (type_entry == g->builtin_types.entry_unreachable) {
2015-12-04 08:30:44 +08:00
buf_init_from_str(out_buf, "__attribute__((__noreturn__)) void");
} else if (type_entry == g->builtin_types.entry_bool) {
buf_init_from_str(out_buf, "unsigned char");
} else if (type_entry == g->builtin_types.entry_void) {
buf_init_from_str(out_buf, "void");
2015-12-01 06:36:58 +08:00
} else {
2015-12-02 12:19:38 +08:00
zig_panic("TODO to_c_type");
}
}
static void generate_h_file(CodeGen *g) {
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->root_out_name));
buf_upcase(export_macro);
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;
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 != VisibModExport)
continue;
2015-12-04 08:30:44 +08:00
Buf return_type_c = BUF_INIT;
to_c_type(g, fn_proto->return_type, &return_type_c);
2015-12-04 08:06:04 +08:00
buf_appendf(&h_buf, "%s %s %s(",
buf_ptr(export_macro),
2015-12-04 08:30:44 +08:00
buf_ptr(&return_type_c),
buf_ptr(&fn_proto->name));
2015-12-04 08:30:44 +08:00
Buf param_type_c = BUF_INIT;
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;
2015-12-04 08:30:44 +08:00
to_c_type(g, param_type, &param_type_c);
buf_appendf(&h_buf, "%s %s",
2015-12-04 08:30:44 +08:00
buf_ptr(&param_type_c),
buf_ptr(&param_decl_node->data.param_decl.name));
if (param_i < fn_proto->params.length - 1)
buf_appendf(&h_buf, ", ");
}
2015-12-04 08:06:04 +08:00
buf_appendf(&h_buf, ")");
} else {
2015-12-04 08:06:04 +08:00
buf_appendf(&h_buf, "void)");
}
2015-12-04 08:06:04 +08:00
buf_appendf(&h_buf, ";\n");
}
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));
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));
}
static const char *get_libc_file(CodeGen *g, const char *file) {
Buf *out_buf = buf_alloc();
os_path_join(g->libc_lib_path, buf_create_from_str(file), out_buf);
return buf_ptr(out_buf);
}
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->root_out_name);
}
2015-11-25 06:01:37 +08:00
Buf out_file_o = BUF_INIT;
2015-11-25 04:51:36 +08:00
buf_init_from_str(&out_file_o, out_file);
2015-11-28 13:13:39 +08:00
if (g->out_type != OutTypeObj) {
buf_append_str(&out_file_o, ".o");
}
2015-11-25 04:51:36 +08:00
2015-11-25 10:07:33 +08:00
char *err_msg = nullptr;
if (LLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(&out_file_o),
LLVMObjectFile, &err_msg))
{
zig_panic("unable to write object file: %s", err_msg);
}
2015-11-25 04:51:36 +08:00
2015-11-28 13:13:39 +08:00
if (g->out_type == OutTypeObj) {
if (g->verbose) {
fprintf(stderr, "OK\n");
}
2015-11-28 13:13:39 +08:00
return;
}
if (g->out_type == OutTypeLib && g->is_static) {
// invoke `ar`
// example:
// # static link into libfoo.a
2016-01-19 07:42:45 +08:00
// ar rcs libfoo.a foo1.o foo2.o
2015-11-28 13:13:39 +08:00
zig_panic("TODO invoke ar");
return;
}
// invoke `ld`
2015-11-25 04:51:36 +08:00
ZigList<const char *> args = {0};
const char *crt1o;
2015-11-25 13:32:26 +08:00
if (g->is_static) {
args.append("-static");
crt1o = "crt1.o";
} else {
crt1o = "Scrt1.o";
2015-11-25 13:32:26 +08:00
}
// TODO don't pass this parameter unless linking with libc
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) {
args.append("-dynamic-linker");
args.append(ZIG_NATIVE_DYNAMIC_LINKER);
}
} else {
args.append("-dynamic-linker");
2015-12-01 05:43:09 +08:00
args.append(buf_ptr(get_dynamic_linker(g->target_machine)));
}
if (g->out_type == OutTypeLib) {
Buf *out_lib_so = buf_sprintf("lib%s.so.%d.%d.%d",
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));
out_file = buf_ptr(out_lib_so);
}
2015-11-25 04:51:36 +08:00
args.append("-o");
args.append(out_file);
bool link_in_crt = (g->link_libc && g->out_type == OutTypeExe);
if (link_in_crt) {
find_libc_path(g);
args.append(get_libc_file(g, crt1o));
args.append(get_libc_file(g, "crti.o"));
}
2015-11-25 04:51:36 +08:00
args.append((const char *)buf_ptr(&out_file_o));
if (link_in_crt) {
args.append(get_libc_file(g, "crtn.o"));
}
2016-02-01 09:32:07 +08:00
for (int i = 0; i < g->lib_dirs.length; i += 1) {
const char *lib_dir = g->lib_dirs.at(i);
args.append("-L");
args.append(lib_dir);
}
auto it = g->link_table.entry_iterator();
for (;;) {
auto *entry = it.next();
if (!entry)
break;
Buf *arg = buf_sprintf("-l%s", buf_ptr(entry->key));
args.append(buf_ptr(arg));
}
if (g->verbose) {
fprintf(stderr, "ld");
for (int i = 0; i < args.length; i += 1) {
fprintf(stderr, " %s", args.at(i));
}
fprintf(stderr, "\n");
}
int return_code;
Buf ld_stderr = BUF_INIT;
Buf ld_stdout = BUF_INIT;
os_exec_process("ld", args, &return_code, &ld_stderr, &ld_stdout);
if (return_code != 0) {
fprintf(stderr, "ld failed with return code %d\n", return_code);
2016-02-01 09:32:07 +08:00
fprintf(stderr, "ld ");
for (int i = 0; i < args.length; i += 1) {
fprintf(stderr, "%s ", args.at(i));
}
fprintf(stderr, "\n%s\n", buf_ptr(&ld_stderr));
exit(1);
2015-12-16 08:11:44 +08:00
} else if (buf_len(&ld_stderr)) {
fprintf(stderr, "%s\n", buf_ptr(&ld_stderr));
}
if (g->out_type == OutTypeLib) {
generate_h_file(g);
}
if (g->verbose) {
fprintf(stderr, "OK\n");
}
}