Merge remote-tracking branch 'origin' into libc-wasi-test
This commit is contained in:
@@ -2144,75 +2144,103 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (abi_class == X64CABIClass_SSE) {
|
||||
// For now only handle structs with only floats/doubles in it.
|
||||
if (ty->id != ZigTypeIdStruct) {
|
||||
if (source_node != nullptr) {
|
||||
give_up_with_c_abi_error(g, source_node);
|
||||
}
|
||||
// otherwise allow codegen code to report a compile error
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
|
||||
if (ty->data.structure.fields[i]->type_entry->id != ZigTypeIdFloat) {
|
||||
if (source_node != nullptr) {
|
||||
give_up_with_c_abi_error(g, source_node);
|
||||
}
|
||||
// otherwise allow codegen code to report a compile error
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The SystemV ABI says that we have to setup 1 FP register per f64.
|
||||
} else if (abi_class == X64CABIClass_AGG) {
|
||||
// The SystemV ABI says that we have to setup 1 register per eightbyte.
|
||||
// So two f32 can be passed in one f64, but 3 f32 have to be passed in 2 FP registers.
|
||||
// To achieve this with LLVM API, we pass multiple f64 parameters to the LLVM function if
|
||||
// the type is bigger than 8 bytes.
|
||||
// Similarly, two i32 can be passed in one i64, but 3 i32 have to be passed in 2 registers.
|
||||
// LLVM does not allow us to control registers in this way, nor to request specific
|
||||
// ABI conventions. So we have to trick it into allocating the right registers, based
|
||||
// on how clang does it.
|
||||
|
||||
// First, we get the LLVM type corresponding to the C abi for the struct, then
|
||||
// we pass each field as an argument.
|
||||
|
||||
// Example:
|
||||
// extern struct {
|
||||
// x: f32,
|
||||
// y: f32,
|
||||
// z: f32,
|
||||
// z: i32,
|
||||
// };
|
||||
// const ptr = (*f64)*Struct;
|
||||
// Register 1: ptr.*
|
||||
// Register 2: (ptr + 1).*
|
||||
// LLVM abi type: { double, i32 }
|
||||
// const ptr = (*abi_type)*Struct;
|
||||
// FP Register 1: abi_type[0]
|
||||
// Register 1: abi_type[1]
|
||||
|
||||
// One floating point register per f64 or 2 f32's
|
||||
size_t number_of_fp_regs = (ty_size + 7) / 8;
|
||||
// However, if the struct fits in one register, then we'll pass it as such
|
||||
size_t number_of_regs = (size_t)ceilf((float)ty_size / (float)8);
|
||||
|
||||
LLVMTypeRef abi_type = get_llvm_c_abi_type(g, ty);
|
||||
|
||||
assert(ty_size <= 16);
|
||||
|
||||
switch (fn_walk->id) {
|
||||
case FnWalkIdAttrs: {
|
||||
fn_walk->data.attrs.gen_i += number_of_fp_regs;
|
||||
fn_walk->data.attrs.gen_i += number_of_regs;
|
||||
break;
|
||||
}
|
||||
case FnWalkIdCall: {
|
||||
LLVMValueRef f64_ptr_to_struct = LLVMBuildBitCast(g->builder, val, LLVMPointerType(LLVMDoubleType(), 0), "");
|
||||
for (uint32_t i = 0; i < number_of_fp_regs; i += 1) {
|
||||
LLVMValueRef index = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, i, false);
|
||||
LLVMValueRef indices[] = { index };
|
||||
LLVMValueRef adjusted_ptr_to_struct = LLVMBuildInBoundsGEP(g->builder, f64_ptr_to_struct, indices, 1, "");
|
||||
LLVMValueRef abi_ptr_to_struct = LLVMBuildBitCast(g->builder, val, LLVMPointerType(abi_type, 0), "");
|
||||
if (number_of_regs == 1) {
|
||||
LLVMValueRef loaded = LLVMBuildLoad(g->builder, abi_ptr_to_struct, "");
|
||||
fn_walk->data.call.gen_param_values->append(loaded);
|
||||
break;
|
||||
}
|
||||
for (uint32_t i = 0; i < number_of_regs; i += 1) {
|
||||
LLVMValueRef zero = LLVMConstInt(LLVMInt32Type(), 0, false);
|
||||
LLVMValueRef index = LLVMConstInt(LLVMInt32Type(), i, false);
|
||||
LLVMValueRef indices[] = { zero, index };
|
||||
LLVMValueRef adjusted_ptr_to_struct = LLVMBuildInBoundsGEP(g->builder, abi_ptr_to_struct, indices, 2, "");
|
||||
LLVMValueRef loaded = LLVMBuildLoad(g->builder, adjusted_ptr_to_struct, "");
|
||||
fn_walk->data.call.gen_param_values->append(loaded);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FnWalkIdTypes: {
|
||||
for (uint32_t i = 0; i < number_of_fp_regs; i += 1) {
|
||||
fn_walk->data.types.gen_param_types->append(get_llvm_type(g, g->builtin_types.entry_f64));
|
||||
if (number_of_regs == 1) {
|
||||
fn_walk->data.types.gen_param_types->append(abi_type);
|
||||
fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, g->builtin_types.entry_f64));
|
||||
break;
|
||||
}
|
||||
for (uint32_t i = 0; i < number_of_regs; i += 1) {
|
||||
fn_walk->data.types.gen_param_types->append(LLVMStructGetTypeAtIndex(abi_type, i));
|
||||
fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, g->builtin_types.entry_f64));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FnWalkIdVars:
|
||||
case FnWalkIdVars: {
|
||||
var->value_ref = build_alloca(g, ty, var->name, var->align_bytes);
|
||||
di_arg_index = fn_walk->data.vars.gen_i;
|
||||
fn_walk->data.vars.gen_i += 1;
|
||||
dest_ty = ty;
|
||||
goto var_ok;
|
||||
}
|
||||
case FnWalkIdInits: {
|
||||
// TODO: Handle exporting functions
|
||||
if (source_node != nullptr) {
|
||||
give_up_with_c_abi_error(g, source_node);
|
||||
// since we're representing the struct differently as an arg, and potentially
|
||||
// splitting it, we have to do some work to put it back together.
|
||||
// the one reg case is straightforward, but if we used two registers we have
|
||||
// to iterate through the struct abi repr fields and load them one by one.
|
||||
if (number_of_regs == 1) {
|
||||
LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i);
|
||||
LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(abi_type, 0);
|
||||
LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, "");
|
||||
gen_store_untyped(g, arg, bitcasted, var->align_bytes, false);
|
||||
} else {
|
||||
LLVMValueRef abi_ptr_to_struct = LLVMBuildBitCast(g->builder, var->value_ref, LLVMPointerType(abi_type, 0), "");
|
||||
for (uint32_t i = 0; i < number_of_regs; i += 1) {
|
||||
LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i + i);
|
||||
LLVMValueRef zero = LLVMConstInt(LLVMInt32Type(), 0, false);
|
||||
LLVMValueRef index = LLVMConstInt(LLVMInt32Type(), i, false);
|
||||
LLVMValueRef indices[] = { zero, index };
|
||||
LLVMValueRef adjusted_ptr_to_struct = LLVMBuildInBoundsGEP(g->builder, abi_ptr_to_struct, indices, 2, "");
|
||||
LLVMBuildStore(g->builder, arg, adjusted_ptr_to_struct);
|
||||
}
|
||||
fn_walk->data.inits.gen_i += 1;
|
||||
}
|
||||
// otherwise allow codegen code to report a compile error
|
||||
return false;
|
||||
if (var->decl_node) {
|
||||
gen_var_debug_decl(g, var);
|
||||
}
|
||||
fn_walk->data.inits.gen_i += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -2656,13 +2684,36 @@ static void gen_async_return(CodeGen *g, Stage1AirInstReturn *instruction) {
|
||||
LLVMBuildRetVoid(g->builder);
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_convert_to_c_abi(CodeGen *g, LLVMValueRef location, LLVMValueRef value) {
|
||||
ZigType *return_type = g->cur_fn->type_entry->data.fn.gen_return_type;
|
||||
size_t size = type_size(g, return_type);
|
||||
|
||||
LLVMTypeRef abi_return_type = get_llvm_c_abi_type(g, return_type);
|
||||
LLVMTypeRef abi_return_type_pointer = LLVMPointerType(abi_return_type, 0);
|
||||
|
||||
if (size < 8) {
|
||||
LLVMValueRef bitcast = LLVMBuildBitCast(g->builder, value, abi_return_type_pointer, "");
|
||||
return LLVMBuildLoad(g->builder, bitcast, "");
|
||||
} else {
|
||||
LLVMTypeRef i8ptr = LLVMPointerType(LLVMInt8Type(), 0);
|
||||
LLVMValueRef bc_location = LLVMBuildBitCast(g->builder, location, i8ptr, "");
|
||||
LLVMValueRef bc_value = LLVMBuildBitCast(g->builder, value, i8ptr, "");
|
||||
|
||||
LLVMValueRef len = LLVMConstInt(LLVMInt64Type(), size, false);
|
||||
ZigLLVMBuildMemCpy(g->builder, bc_location, 8, bc_value, return_type->abi_align, len, false);
|
||||
return LLVMBuildLoad(g->builder, location, "");
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_return(CodeGen *g, Stage1Air *executable, Stage1AirInstReturn *instruction) {
|
||||
if (fn_is_async(g->cur_fn)) {
|
||||
gen_async_return(g, instruction);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) {
|
||||
FnTypeId *fn_type_id = &g->cur_fn->type_entry->data.fn.fn_type_id;
|
||||
|
||||
if (want_first_arg_sret(g, fn_type_id)) {
|
||||
if (instruction->operand == nullptr) {
|
||||
LLVMBuildRetVoid(g->builder);
|
||||
return nullptr;
|
||||
@@ -2673,6 +2724,16 @@ static LLVMValueRef ir_render_return(CodeGen *g, Stage1Air *executable, Stage1Ai
|
||||
ZigType *return_type = instruction->operand->value->type;
|
||||
gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value);
|
||||
LLVMBuildRetVoid(g->builder);
|
||||
} else if (fn_returns_c_abi_small_struct(fn_type_id)) {
|
||||
LLVMValueRef location = g->cur_fn->abi_return_value;
|
||||
if (instruction->operand == nullptr) {
|
||||
LLVMValueRef converted = gen_convert_to_c_abi(g, location, g->cur_ret_ptr);
|
||||
LLVMBuildRet(g->builder, converted);
|
||||
} else {
|
||||
LLVMValueRef value = ir_llvm_value(g, instruction->operand);
|
||||
LLVMValueRef converted = gen_convert_to_c_abi(g, location, value);
|
||||
LLVMBuildRet(g->builder, converted);
|
||||
}
|
||||
} else if (g->cur_fn->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync &&
|
||||
handle_is_ptr(g, g->cur_fn->type_entry->data.fn.fn_type_id.return_type))
|
||||
{
|
||||
@@ -3250,6 +3311,30 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
|
||||
case IrBinOpRemMod:
|
||||
return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
|
||||
op1_value, op2_value, operand_type, RemKindMod);
|
||||
case IrBinOpMaximum:
|
||||
if (scalar_type->id == ZigTypeIdFloat) {
|
||||
return ZigLLVMBuildMaxNum(g->builder, op1_value, op2_value, "");
|
||||
} else if (scalar_type->id == ZigTypeIdInt) {
|
||||
if (scalar_type->data.integral.is_signed) {
|
||||
return ZigLLVMBuildSMax(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
return ZigLLVMBuildUMax(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
case IrBinOpMinimum:
|
||||
if (scalar_type->id == ZigTypeIdFloat) {
|
||||
return ZigLLVMBuildMinNum(g->builder, op1_value, op2_value, "");
|
||||
} else if (scalar_type->id == ZigTypeIdInt) {
|
||||
if (scalar_type->data.integral.is_signed) {
|
||||
return ZigLLVMBuildSMin(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
return ZigLLVMBuildUMin(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@@ -4656,6 +4741,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, Stage1Air *executable, Stage1AirI
|
||||
} else if (first_arg_ret) {
|
||||
ZigLLVMSetCallSret(result, get_llvm_type(g, src_return_type));
|
||||
return result_loc;
|
||||
} else if (fn_returns_c_abi_small_struct(fn_type_id)) {
|
||||
LLVMTypeRef abi_type = get_llvm_c_abi_type(g, src_return_type);
|
||||
LLVMTypeRef abi_type_ptr = LLVMPointerType(abi_type, 0);
|
||||
LLVMValueRef bitcast = LLVMBuildBitCast(g->builder, result_loc, abi_type_ptr, "");
|
||||
LLVMBuildStore(g->builder, result, bitcast);
|
||||
return result_loc;
|
||||
} else if (handle_is_ptr(g, src_return_type)) {
|
||||
LLVMValueRef store_instr = LLVMBuildStore(g->builder, result, result_loc);
|
||||
LLVMSetAlignment(store_instr, get_ptr_align(g, instruction->result_loc->value->type));
|
||||
@@ -5164,6 +5255,13 @@ static LLVMValueRef ir_render_shuffle_vector(CodeGen *g, Stage1Air *executable,
|
||||
llvm_mask_value, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_select(CodeGen *g, Stage1Air *executable, Stage1AirInstSelect *instruction) {
|
||||
LLVMValueRef pred = ir_llvm_value(g, instruction->pred);
|
||||
LLVMValueRef a = ir_llvm_value(g, instruction->a);
|
||||
LLVMValueRef b = ir_llvm_value(g, instruction->b);
|
||||
return LLVMBuildSelect(g->builder, pred, a, b, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_splat(CodeGen *g, Stage1Air *executable, Stage1AirInstSplat *instruction) {
|
||||
ZigType *result_type = instruction->base.value->type;
|
||||
ir_assert(result_type->id == ZigTypeIdVector, &instruction->base);
|
||||
@@ -7017,6 +7115,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, Stage1Air *executable, Sta
|
||||
return ir_render_spill_end(g, executable, (Stage1AirInstSpillEnd *)instruction);
|
||||
case Stage1AirInstIdShuffleVector:
|
||||
return ir_render_shuffle_vector(g, executable, (Stage1AirInstShuffleVector *) instruction);
|
||||
case Stage1AirInstIdSelect:
|
||||
return ir_render_select(g, executable, (Stage1AirInstSelect *) instruction);
|
||||
case Stage1AirInstIdSplat:
|
||||
return ir_render_splat(g, executable, (Stage1AirInstSplat *) instruction);
|
||||
case Stage1AirInstIdVectorExtractElem:
|
||||
@@ -8260,6 +8360,11 @@ static void do_code_gen(CodeGen *g) {
|
||||
g->cur_err_ret_trace_val_stack = nullptr;
|
||||
}
|
||||
|
||||
if (fn_returns_c_abi_small_struct(fn_type_id)) {
|
||||
LLVMTypeRef abi_type = get_llvm_c_abi_type(g, fn_type_id->return_type);
|
||||
fn_table_entry->abi_return_value = LLVMBuildAlloca(g->builder, abi_type, "");
|
||||
}
|
||||
|
||||
if (!is_async) {
|
||||
// allocate async frames for nosuspend calls & awaits to async functions
|
||||
ZigType *largest_call_frame_type = nullptr;
|
||||
@@ -8763,6 +8868,9 @@ static void define_builtin_types(CodeGen *g) {
|
||||
case ZigLLVM_sparcv9:
|
||||
add_fp_entry(g, "c_longdouble", 128, LLVMFP128Type(), &g->builtin_types.entry_c_longdouble);
|
||||
break;
|
||||
case ZigLLVM_systemz:
|
||||
add_fp_entry(g, "c_longdouble", 128, LLVMDoubleType(), &g->builtin_types.entry_c_longdouble);
|
||||
break;
|
||||
case ZigLLVM_avr:
|
||||
// It's either a float or a double, depending on a toolchain switch
|
||||
add_fp_entry(g, "c_longdouble", 64, LLVMDoubleType(), &g->builtin_types.entry_c_longdouble);
|
||||
@@ -8922,6 +9030,7 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
|
||||
create_builtin_fn(g, BuiltinFnIdVectorType, "Vector", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdShuffle, "shuffle", 4);
|
||||
create_builtin_fn(g, BuiltinFnIdSelect, "select", 4);
|
||||
create_builtin_fn(g, BuiltinFnIdSplat, "splat", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdSetRuntimeSafety, "setRuntimeSafety", 1);
|
||||
@@ -8982,6 +9091,8 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSrc, "src", 0);
|
||||
create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdMinimum, "minimum", 2);
|
||||
}
|
||||
|
||||
static const char *bool_to_str(bool b) {
|
||||
|
||||
Reference in New Issue
Block a user