mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
658 lines
21 KiB
C
658 lines
21 KiB
C
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
|
// a copy of which can be found in the LICENSE file.
|
|
|
|
|
|
#include "llvm_codegen_internal.h"
|
|
|
|
static void llvm_emit_param_attributes(GenContext *context, LLVMValueRef function, ABIArgInfo *info, bool is_return, int index, int last_index);
|
|
static inline void llvm_emit_return_value(GenContext *context, LLVMValueRef value);
|
|
static void llvm_expand_from_args(GenContext *c, Type *type, LLVMValueRef ref, unsigned *index);
|
|
static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsigned *index);
|
|
|
|
bool llvm_emit_check_block_branch(GenContext *context)
|
|
{
|
|
if (!context->current_block) return false;
|
|
// If it's not used, we can delete the previous block and skip the branch.
|
|
// Unless it is the entry block or a label target for jumps
|
|
// These empty blocks will occur when doing branches.
|
|
// Consider:
|
|
// while (1)
|
|
// {
|
|
// break;
|
|
// break;
|
|
// }
|
|
// Naively we'd output
|
|
// br label %for.cond - 1st break
|
|
// br label %for.cond - 2nd break
|
|
// br label %for.cond - end of scope
|
|
//
|
|
// The fix is to introduce a new block after a break:
|
|
// br label %for.cond
|
|
// jmp:
|
|
// br label %for.cond
|
|
// jmp.1:
|
|
// br label %for.cond
|
|
//
|
|
// But this leaves us with blocks that have no parent.
|
|
// Consequently we will delete those and realize that
|
|
// we then have no need for emitting a br.
|
|
if (!context->current_block_is_target
|
|
&& !LLVMGetFirstUse(LLVMBasicBlockAsValue(context->current_block)))
|
|
{
|
|
LLVMDeleteBasicBlock(context->current_block);
|
|
context->current_block = NULL;
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
void llvm_emit_br(GenContext *c, LLVMBasicBlockRef next_block)
|
|
{
|
|
if (!llvm_emit_check_block_branch(c)) return;
|
|
c->current_block = NULL;
|
|
LLVMBuildBr(c->builder, next_block);
|
|
}
|
|
|
|
|
|
|
|
void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block)
|
|
{
|
|
assert(context->current_block);
|
|
assert(value->kind == BE_BOOLEAN);
|
|
LLVMBuildCondBr(context->builder, value->value, then_block, else_block);
|
|
LLVMClearInsertionPosition(context->builder);
|
|
context->current_block = NULL;
|
|
context->current_block_is_target = false;
|
|
}
|
|
|
|
|
|
void llvm_emit_block(GenContext *context, LLVMBasicBlockRef next_block)
|
|
{
|
|
assert(context->current_block == NULL);
|
|
LLVMAppendExistingBasicBlock(context->function, next_block);
|
|
LLVMPositionBuilderAtEnd(context->builder, next_block);
|
|
context->current_block = next_block;
|
|
context->current_block_is_target = false;
|
|
}
|
|
|
|
static void llvm_expand_from_args(GenContext *c, Type *type, LLVMValueRef ref, unsigned *index)
|
|
{
|
|
switch (type->type_kind)
|
|
{
|
|
case TYPE_ARRAY:
|
|
for (unsigned i = 0; i < type->array.len; i++)
|
|
{
|
|
LLVMValueRef indices[2] = { llvm_get_zero(c, type_uint), llvm_const_int(c, type_uint, i) };
|
|
LLVMValueRef target = LLVMBuildInBoundsGEP2(c->builder, llvm_get_type(c, type), ref, indices, 2, "");
|
|
LLVMValueRef cast_addr = llvm_emit_bitcast(c, target, type_get_ptr(type->array.base));
|
|
llvm_expand_from_args(c, type->array.base, cast_addr, index);
|
|
}
|
|
return;
|
|
case TYPE_STRUCT:
|
|
{
|
|
Decl **members = type->decl->strukt.members;
|
|
VECEACH(members, i)
|
|
{
|
|
LLVMValueRef indices[2] = { llvm_get_zero(c, type_uint), llvm_const_int(c, type_uint, i) };
|
|
LLVMValueRef target = LLVMBuildInBoundsGEP2(c->builder, llvm_get_type(c, type), ref, indices, 2, "");
|
|
LLVMValueRef cast_addr = llvm_emit_bitcast(c, target, type_get_ptr(members[i]->type));
|
|
llvm_expand_from_args(c, members[i]->type, cast_addr, index);
|
|
}
|
|
return;
|
|
}
|
|
case TYPE_UNION:
|
|
{
|
|
Type *largest_type = type_find_largest_union_element(type);
|
|
LLVMValueRef cast_addr = llvm_emit_bitcast(c, ref, type_get_ptr(largest_type));
|
|
llvm_expand_from_args(c, largest_type, cast_addr, index);
|
|
return;
|
|
}
|
|
default:
|
|
LLVMBuildStore(c->builder, llvm_get_next_param(c, index), ref);
|
|
return;
|
|
}
|
|
}
|
|
|
|
LLVMValueRef llvm_get_next_param(GenContext *context, unsigned *index)
|
|
{
|
|
return LLVMGetParam(context->function, (*index)++);
|
|
}
|
|
|
|
|
|
static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsigned *index)
|
|
{
|
|
ABIArgInfo *info = decl->var.abi_info;
|
|
switch (info->kind)
|
|
{
|
|
case ABI_ARG_IGNORE:
|
|
return;
|
|
case ABI_ARG_INDIRECT:
|
|
{
|
|
// A simple memcopy, with alignment respected.
|
|
LLVMValueRef pointer = llvm_get_next_param(c, index);
|
|
llvm_emit_memcpy_to_decl(c, decl, pointer, info->indirect.realignment);
|
|
return;
|
|
}
|
|
case ABI_ARG_EXPAND_COERCE:
|
|
{
|
|
// Create the expand type:
|
|
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
|
|
LLVMValueRef temp = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(coerce_type, 0), "coerce");
|
|
LLVMValueRef gep_first = LLVMBuildStructGEP2(c->builder, coerce_type, temp, info->coerce_expand.lo_index, "first");
|
|
llvm_store_aligned(c, gep_first, llvm_get_next_param(c, index), 0);
|
|
if (info->coerce_expand.hi)
|
|
{
|
|
LLVMValueRef gep_second = LLVMBuildStructGEP2(c->builder, coerce_type, temp, info->coerce_expand.hi_index, "second");
|
|
llvm_store_aligned(c, gep_second, llvm_get_next_param(c, index), 0);
|
|
}
|
|
break;
|
|
}
|
|
case ABI_ARG_DIRECT_PAIR:
|
|
{
|
|
// Here we do the following transform:
|
|
// lo, hi -> { lo, hi } -> struct
|
|
LLVMTypeRef lo = llvm_abi_type(c, info->direct_pair.lo);
|
|
LLVMTypeRef hi = llvm_abi_type(c, info->direct_pair.hi);
|
|
LLVMTypeRef struct_type = llvm_get_twostruct(c, lo, hi);
|
|
AlignSize decl_alignment = decl->alignment;
|
|
// Cast to { lo, hi }
|
|
LLVMValueRef cast = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(struct_type, 0), "pair");
|
|
// Point to the lo value.
|
|
LLVMValueRef lo_ptr = LLVMBuildStructGEP2(c->builder, struct_type, cast, 0, "lo");
|
|
// Store it in the struct.
|
|
AlignSize lo_alignment = MIN(llvm_abi_alignment(c, lo), decl_alignment);
|
|
llvm_store_aligned(c, lo_ptr, llvm_get_next_param(c, index), lo_alignment);
|
|
// Point to the hi value.
|
|
LLVMValueRef hi_ptr = LLVMBuildStructGEP2(c->builder, struct_type, cast, 1, "hi");
|
|
// Store it in the struct.
|
|
AlignSize hi_alignment = MIN(llvm_abi_alignment(c, hi), decl_alignment);
|
|
llvm_store_aligned(c, hi_ptr, llvm_get_next_param(c, index), hi_alignment);
|
|
return;
|
|
}
|
|
case ABI_ARG_DIRECT_COERCE:
|
|
{
|
|
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
|
|
if (!coerce_type || coerce_type == llvm_get_type(c, decl->type))
|
|
{
|
|
llvm_store_aligned_decl(c, decl, llvm_get_next_param(c, index));
|
|
return;
|
|
}
|
|
// Cast to the coerce type.
|
|
LLVMValueRef cast = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(coerce_type, 0), "coerce");
|
|
|
|
// If we're not flattening, we simply do a store.
|
|
if (!abi_info_should_flatten(info))
|
|
{
|
|
LLVMValueRef param = llvm_get_next_param(c, index);
|
|
// Store it with the alignment of the decl.
|
|
llvm_store_aligned(c, cast, param, decl->alignment);
|
|
return;
|
|
}
|
|
|
|
// In this case we've been flattening the parameter into multiple registers.
|
|
LLVMTypeRef element_type = llvm_abi_type(c, info->direct_coerce.type);
|
|
// Store each expanded parameter.
|
|
for (unsigned idx = 0; idx < info->direct_coerce.elements; idx++)
|
|
{
|
|
LLVMValueRef element_ptr = LLVMBuildStructGEP2(c->builder, coerce_type, cast, idx, "");
|
|
LLVMValueRef value = llvm_get_next_param(c, index);
|
|
|
|
llvm_store_aligned(c, element_ptr, value, MIN(llvm_abi_alignment(c, element_type), decl->alignment));
|
|
}
|
|
return;
|
|
}
|
|
case ABI_ARG_EXPAND:
|
|
{
|
|
llvm_expand_from_args(c, decl->type, decl->backend_ref, index);
|
|
if (info->expand.padding_type)
|
|
{
|
|
// Skip the pad.
|
|
llvm_get_next_param(c, index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static inline void llvm_emit_parameter(GenContext *context, Decl *decl, unsigned *index, unsigned real_index)
|
|
{
|
|
assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM);
|
|
|
|
// Allocate room on stack, but do not copy.
|
|
llvm_emit_and_set_decl_alloca(context, decl);
|
|
llvm_process_parameter_value(context, decl, index);
|
|
if (llvm_use_debug(context))
|
|
{
|
|
llvm_emit_debug_parameter(context, decl, real_index);
|
|
}
|
|
}
|
|
|
|
static inline void llvm_emit_return_value(GenContext *context, LLVMValueRef value)
|
|
{
|
|
if (!value)
|
|
{
|
|
LLVMBuildRetVoid(context->builder);
|
|
}
|
|
else
|
|
{
|
|
LLVMBuildRet(context->builder, value);
|
|
}
|
|
context->current_block = NULL;
|
|
context->current_block_is_target = false;
|
|
}
|
|
|
|
void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable)
|
|
{
|
|
FunctionSignature *signature = &c->cur_func_decl->func_decl.function_signature;
|
|
ABIArgInfo *info = signature->ret_abi_info;
|
|
|
|
// If we have a failable it's always the return argument, so we need to copy
|
|
// the return value into the return value holder.
|
|
LLVMValueRef return_out = c->return_out;
|
|
Type *return_type = signature->rtype->type;
|
|
|
|
BEValue no_fail;
|
|
|
|
// In this case we use the failable as the actual return.
|
|
if (signature->failable)
|
|
{
|
|
if (return_value && return_value->value)
|
|
{
|
|
|
|
llvm_store_bevalue_aligned(c, c->return_out, return_value, 0);
|
|
}
|
|
return_out = c->failable_out;
|
|
return_type = type_error;
|
|
if (!failable)
|
|
{
|
|
llvm_value_set(&no_fail, LLVMConstNull(llvm_get_type(c, type_error)), type_error);
|
|
failable = &no_fail;
|
|
}
|
|
return_value = failable;
|
|
info = signature->failable_abi_info;
|
|
}
|
|
|
|
switch (info->kind)
|
|
{
|
|
case ABI_ARG_INDIRECT:
|
|
llvm_store_bevalue_aligned(c, return_out, return_value, info->indirect.realignment);
|
|
llvm_emit_return_value(c, NULL);
|
|
return;
|
|
case ABI_ARG_IGNORE:
|
|
llvm_emit_return_value(c, NULL);
|
|
return;
|
|
case ABI_ARG_EXPAND:
|
|
// Expands to multiple slots -
|
|
// Not applicable to return values.
|
|
UNREACHABLE
|
|
case ABI_ARG_EXPAND_COERCE:
|
|
{
|
|
// Pick the return as an address.
|
|
llvm_value_addr(c, return_value);
|
|
// Get the coerce type.
|
|
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
|
|
// Create the new pointer
|
|
LLVMValueRef coerce = LLVMBuildBitCast(c->builder, return_value->value, coerce_type, "");
|
|
// We might have only one value, in that case, build a GEP to that one.
|
|
LLVMValueRef lo_val;
|
|
unsigned alignment;
|
|
LLVMValueRef lo = llvm_emit_struct_gep_raw(c, coerce, coerce_type, info->coerce_expand.lo_index,
|
|
return_value->alignment,
|
|
info->coerce_expand.offset_lo, &alignment);
|
|
LLVMTypeRef lo_type = llvm_abi_type(c, info->coerce_expand.lo);
|
|
lo_val = llvm_emit_load_aligned(c, lo_type, lo, alignment, "");
|
|
|
|
// We're done if there's a single field.
|
|
if (!info->coerce_expand.hi)
|
|
{
|
|
llvm_emit_return_value(c, lo_val);
|
|
return;
|
|
}
|
|
|
|
// Let's make a first class aggregate
|
|
LLVMValueRef hi = llvm_emit_struct_gep_raw(c, coerce, coerce_type, info->coerce_expand.hi_index,
|
|
return_value->alignment,
|
|
info->coerce_expand.offset_hi, &alignment);
|
|
LLVMTypeRef hi_type = llvm_abi_type(c, info->coerce_expand.hi);
|
|
LLVMValueRef hi_val = llvm_emit_load_aligned(c, hi_type, hi, alignment, "");
|
|
|
|
LLVMTypeRef unpadded_type = llvm_get_twostruct(c, lo_type, hi_type);
|
|
LLVMValueRef composite = LLVMGetUndef(unpadded_type);
|
|
|
|
composite = LLVMBuildInsertValue(c->builder, composite, lo_val, 0, "");
|
|
composite = LLVMBuildInsertValue(c->builder, composite, hi_val, 1, "");
|
|
|
|
// And return that unpadded result
|
|
llvm_emit_return_value(c, composite);
|
|
break;
|
|
}
|
|
case ABI_ARG_DIRECT_PAIR:
|
|
case ABI_ARG_DIRECT_COERCE:
|
|
{
|
|
LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info);
|
|
if (!coerce_type || coerce_type == llvm_get_type(c, return_type))
|
|
{
|
|
// The normal return
|
|
llvm_emit_return_value(c, llvm_value_rvalue_store(c, return_value));
|
|
return;
|
|
}
|
|
assert(!abi_info_should_flatten(info));
|
|
llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, return_type));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void llvm_emit_return_implicit(GenContext *c)
|
|
{
|
|
if (c->cur_func_decl->func_decl.function_signature.rtype->type != type_void)
|
|
{
|
|
LLVMBuildUnreachable(c->builder);
|
|
return;
|
|
}
|
|
if (!c->cur_func_decl->func_decl.function_signature.failable)
|
|
{
|
|
llvm_emit_return_abi(c, NULL, NULL);
|
|
return;
|
|
}
|
|
BEValue value;
|
|
llvm_value_set(&value, LLVMConstNull(llvm_get_type(c, type_error)), type_error);
|
|
llvm_emit_return_abi(c, NULL, &value);
|
|
}
|
|
|
|
void llvm_emit_function_body(GenContext *context, Decl *decl)
|
|
{
|
|
DEBUG_LOG("Generating function %s.", decl->external_name);
|
|
assert(decl->backend_ref);
|
|
|
|
bool emit_debug = llvm_use_debug(context);
|
|
LLVMValueRef prev_function = context->function;
|
|
LLVMBuilderRef prev_builder = context->builder;
|
|
|
|
context->error_var = NULL;
|
|
context->catch_block = NULL;
|
|
|
|
context->function = decl->backend_ref;
|
|
if (emit_debug)
|
|
{
|
|
context->debug.function = LLVMGetSubprogram(context->function);
|
|
}
|
|
|
|
context->cur_func_decl = decl;
|
|
|
|
LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(context->context, context->function, "entry");
|
|
context->current_block = entry;
|
|
context->current_block_is_target = true;
|
|
context->block_return_exit = NULL;
|
|
context->in_block = 0;
|
|
context->builder = LLVMCreateBuilderInContext(context->context);
|
|
LLVMPositionBuilderAtEnd(context->builder, entry);
|
|
|
|
LLVMValueRef alloca_point = LLVMBuildAlloca(context->builder, LLVMInt32TypeInContext(context->context), "alloca_point");
|
|
context->alloca_point = alloca_point;
|
|
|
|
FunctionSignature *signature = &decl->func_decl.function_signature;
|
|
unsigned arg = 0;
|
|
|
|
if (emit_debug)
|
|
{
|
|
llvm_debug_scope_push(context, context->debug.function);
|
|
}
|
|
|
|
if (signature->failable && signature->failable_abi_info->kind == ABI_ARG_INDIRECT)
|
|
{
|
|
context->failable_out = LLVMGetParam(context->function, arg++);
|
|
}
|
|
else
|
|
{
|
|
context->failable_out = NULL;
|
|
}
|
|
if (signature->ret_abi_info && signature->ret_abi_info->kind == ABI_ARG_INDIRECT)
|
|
{
|
|
context->return_out = LLVMGetParam(context->function, arg++);
|
|
}
|
|
else
|
|
{
|
|
context->return_out = NULL;
|
|
if (signature->ret_abi_info && signature->failable)
|
|
{
|
|
context->return_out = LLVMGetParam(context->function, arg++);
|
|
}
|
|
}
|
|
|
|
|
|
// Generate LLVMValueRef's for all parameters, so we can use them as local vars in code
|
|
VECEACH(decl->func_decl.function_signature.params, i)
|
|
{
|
|
llvm_emit_parameter(context, decl->func_decl.function_signature.params[i], &arg, i);
|
|
}
|
|
|
|
LLVMSetCurrentDebugLocation2(context->builder, NULL);
|
|
|
|
VECEACH(decl->func_decl.body->compound_stmt.stmts, i)
|
|
{
|
|
llvm_emit_stmt(context, decl->func_decl.body->compound_stmt.stmts[i]);
|
|
}
|
|
|
|
if (context->current_block && !LLVMGetFirstInstruction(context->current_block) && !LLVMGetFirstUse(LLVMBasicBlockAsValue(context->current_block)))
|
|
{
|
|
LLVMBasicBlockRef prev_block = LLVMGetPreviousBasicBlock(context->current_block);
|
|
LLVMDeleteBasicBlock(context->current_block);
|
|
context->current_block = prev_block;
|
|
LLVMPositionBuilderAtEnd(context->builder, context->current_block);
|
|
}
|
|
// Insert a return (and defer) if needed.
|
|
if (context->current_block && !LLVMGetBasicBlockTerminator(context->current_block))
|
|
{
|
|
assert(!decl->func_decl.body->compound_stmt.defer_list.end);
|
|
llvm_emit_defer(context, decl->func_decl.body->compound_stmt.defer_list.start, 0);
|
|
llvm_emit_return_implicit(context);
|
|
}
|
|
|
|
// erase alloca point
|
|
if (LLVMGetInstructionParent(alloca_point))
|
|
{
|
|
context->alloca_point = NULL;
|
|
LLVMInstructionEraseFromParent(alloca_point);
|
|
}
|
|
|
|
LLVMDisposeBuilder(context->builder);
|
|
|
|
if (llvm_use_debug(context))
|
|
{
|
|
llvm_debug_scope_pop(context);
|
|
}
|
|
|
|
context->builder = prev_builder;
|
|
context->function = prev_function;
|
|
}
|
|
|
|
static void llvm_emit_param_attributes(GenContext *context, LLVMValueRef function, ABIArgInfo *info, bool is_return, int index, int last_index)
|
|
{
|
|
assert(last_index == index || info->kind == ABI_ARG_DIRECT_PAIR || info->kind == ABI_ARG_IGNORE
|
|
|| info->kind == ABI_ARG_EXPAND);
|
|
|
|
if (info->attributes.zeroext)
|
|
{
|
|
// Direct only
|
|
assert(index == last_index);
|
|
llvm_attribute_add(context, function, attribute_zext, index);
|
|
}
|
|
if (info->attributes.signext)
|
|
{
|
|
// Direct only
|
|
assert(index == last_index);
|
|
llvm_attribute_add(context, function, attribute_sext, index);
|
|
}
|
|
if (info->attributes.by_reg)
|
|
{
|
|
llvm_attribute_add_range(context, function, attribute_inreg, index, last_index);
|
|
}
|
|
switch (info->kind)
|
|
{
|
|
case ABI_ARG_EXPAND:
|
|
case ABI_ARG_IGNORE:
|
|
case ABI_ARG_DIRECT_COERCE:
|
|
case ABI_ARG_DIRECT_PAIR:
|
|
case ABI_ARG_EXPAND_COERCE:
|
|
break;
|
|
case ABI_ARG_INDIRECT:
|
|
if (info->indirect.realignment)
|
|
{
|
|
llvm_attribute_add_int(context, function, attribute_align, info->indirect.realignment, index);
|
|
}
|
|
if (is_return)
|
|
{
|
|
llvm_attribute_add(context, function, attribute_sret, 1);
|
|
}
|
|
else
|
|
{
|
|
// TODO then type attributes are added to LLVM-C, use that for byval.
|
|
if (info->indirect.by_val_type) llvm_attribute_add(context, function, attribute_byval, index);
|
|
if (!info->indirect.realignment)
|
|
{
|
|
llvm_attribute_add_int(context, function, attribute_align, type_abi_alignment(info->indirect.by_val_type), index);
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
void llvm_emit_function_decl(GenContext *c, Decl *decl)
|
|
{
|
|
assert(decl->decl_kind == DECL_FUNC);
|
|
// Resolve function backend type for function.
|
|
LLVMValueRef function = LLVMAddFunction(c->module, decl->extname ?: decl->external_name, llvm_get_type(c, decl->type));
|
|
decl->backend_ref = function;
|
|
FunctionSignature *signature = &decl->func_decl.function_signature;
|
|
FunctionSignature *type_signature = decl->type->func.signature;
|
|
|
|
// We only resolve 1 function signature, so we might have functions
|
|
// with the same signature (but different default values!)
|
|
// that we have in common. So overwrite the data from the type here.
|
|
if (signature != type_signature)
|
|
{
|
|
// Store the params.
|
|
Decl **params = signature->params;
|
|
// Copy the rest.
|
|
*signature = *type_signature;
|
|
signature->params = params;
|
|
VECEACH(params, i)
|
|
{
|
|
Decl *sig_param = type_signature->params[i];
|
|
Decl *param = params[i];
|
|
param->var.abi_info = sig_param->var.abi_info;
|
|
}
|
|
signature->params = params;
|
|
}
|
|
|
|
ABIArgInfo *ret_abi_info = signature->failable_abi_info ?: signature->ret_abi_info;
|
|
llvm_emit_param_attributes(c, function, ret_abi_info, true, 0, 0);
|
|
Decl **params = signature->params;
|
|
if (signature->failable_abi_info && signature->ret_abi_info)
|
|
{
|
|
ABIArgInfo *info = signature->ret_abi_info;
|
|
llvm_emit_param_attributes(c, function, info, false, info->param_index_start + 1, info->param_index_end);
|
|
}
|
|
VECEACH(params, i)
|
|
{
|
|
Decl *param = params[i];
|
|
ABIArgInfo *info = param->var.abi_info;
|
|
llvm_emit_param_attributes(c, function, info, false, info->param_index_start + 1, info->param_index_end);
|
|
}
|
|
if (decl->func_decl.attr_inline)
|
|
{
|
|
llvm_attribute_add(c, function, attribute_alwaysinline, -1);
|
|
}
|
|
if (decl->func_decl.attr_noinline)
|
|
{
|
|
llvm_attribute_add(c, function, attribute_noinline, -1);
|
|
}
|
|
if (decl->func_decl.attr_noreturn)
|
|
{
|
|
llvm_attribute_add(c, function, attribute_noreturn, -1);
|
|
}
|
|
if (decl->alignment)
|
|
{
|
|
llvm_set_alignment(function, decl->alignment);
|
|
}
|
|
if (decl->section)
|
|
{
|
|
LLVMSetSection(function, decl->section);
|
|
}
|
|
llvm_attribute_add(c, function, attribute_nounwind, -1);
|
|
|
|
if (decl->func_decl.attr_stdcall && (platform_target.os == OS_TYPE_WIN32))
|
|
{
|
|
LLVMSetFunctionCallConv(function, LLVMX86StdcallCallConv);
|
|
LLVMSetDLLStorageClass(function, LLVMDLLImportStorageClass);
|
|
}
|
|
|
|
switch (decl->visibility)
|
|
{
|
|
case VISIBLE_EXTERN:
|
|
LLVMSetLinkage(function, decl->func_decl.attr_weak ? LLVMExternalWeakLinkage : LLVMExternalLinkage);
|
|
LLVMSetVisibility(function, LLVMDefaultVisibility);
|
|
break;
|
|
case VISIBLE_PUBLIC:
|
|
case VISIBLE_MODULE:
|
|
if (decl->func_decl.attr_weak) LLVMSetLinkage(function, LLVMWeakAnyLinkage);
|
|
LLVMSetVisibility(function, LLVMDefaultVisibility);
|
|
break;
|
|
case VISIBLE_LOCAL:
|
|
LLVMSetLinkage(function, decl->func_decl.attr_weak ? LLVMLinkerPrivateWeakLinkage : LLVMInternalLinkage);
|
|
LLVMSetVisibility(function, LLVMDefaultVisibility);
|
|
break;;
|
|
}
|
|
if (llvm_use_debug(c))
|
|
{
|
|
llvm_emit_debug_function(c, decl);
|
|
}
|
|
}
|
|
|
|
|
|
void llvm_emit_methods(GenContext *c, Decl **methods)
|
|
{
|
|
VECEACH(methods, i)
|
|
{
|
|
Decl *decl = methods[i];
|
|
if (decl->decl_kind == DECL_MACRO) continue;
|
|
llvm_emit_function_decl(c, decl);
|
|
}
|
|
}
|
|
|
|
void llvm_emit_extern_decl(GenContext *context, Decl *decl)
|
|
{
|
|
switch (decl->decl_kind)
|
|
{
|
|
case DECL_POISONED:
|
|
UNREACHABLE;
|
|
case DECL_FUNC:
|
|
decl->backend_ref = LLVMAddFunction(context->module, decl->extname ?: decl->external_name,
|
|
llvm_get_type(context, decl->type));
|
|
LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility);
|
|
break;
|
|
case DECL_VAR:
|
|
decl->backend_ref = LLVMAddGlobal(context->module, llvm_get_type(context, decl->type), decl->extname ?: decl->external_name);
|
|
LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility);
|
|
break;
|
|
case DECL_STRUCT:
|
|
case DECL_UNION:
|
|
case DECL_ERR:
|
|
llvm_emit_methods(context, decl->methods);
|
|
llvm_get_type(context, decl->type);
|
|
// TODO // Fix typeid
|
|
break;
|
|
case DECL_ENUM:
|
|
llvm_emit_methods(context, decl->methods);
|
|
// TODO // Fix typeid
|
|
return;
|
|
case DECL_TYPEDEF:
|
|
case DECL_DISTINCT:
|
|
case NON_TYPE_DECLS:
|
|
case DECL_INTERFACE:
|
|
case DECL_ENUM_CONSTANT:
|
|
return;
|
|
}
|
|
}
|