mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
LLVM: rework calling convention lowering
The previous implementation of calling conventions was hacky and broken. This commit reworks lowerFnParamTy into iterateParamTypes which returns enum tags indicating how to handle each parameter. This is then used in the three places that matter: * lowering a function type to llvm type * converting function parameters to the canonical type representation (with respect to isByRef). * converting canonical type representation to function arguments at callsites (again with respect to isByRef). As a result, we are one step closer to the C ABI tests passing. Before this commit, attempting to build them crashed the compiler. I isolated the broken function and verified that it now is lowered correctly. I will keep working on this one piece at a time until all the C ABI tests pass, and then I will enable all of them in the CI.
This commit is contained in:
parent
1a92264b3d
commit
1697a6f044
@ -682,25 +682,130 @@ pub const Object = struct {
|
||||
else
|
||||
null;
|
||||
|
||||
// This is the list of args we will use that correspond directly to the AIR arg
|
||||
// instructions. Depending on the calling convention, this list is not necessarily
|
||||
// a bijection with the actual LLVM parameters of the function.
|
||||
var args = std.ArrayList(*const llvm.Value).init(gpa);
|
||||
defer args.deinit();
|
||||
|
||||
const param_offset = @as(c_uint, @boolToInt(ret_ptr != null)) + @boolToInt(err_return_tracing);
|
||||
for (fn_info.param_types) |param_ty| {
|
||||
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
|
||||
{
|
||||
var llvm_arg_i = @as(c_uint, @boolToInt(ret_ptr != null)) + @boolToInt(err_return_tracing);
|
||||
var it = iterateParamTypes(&dg, fn_info);
|
||||
while (it.next()) |lowering| switch (lowering) {
|
||||
.no_bits => continue,
|
||||
.byval => {
|
||||
const param_ty = fn_info.param_types[it.zig_index - 1];
|
||||
const param = llvm_func.getParam(llvm_arg_i);
|
||||
llvm_arg_i += 1;
|
||||
|
||||
const llvm_arg_i = @intCast(c_uint, args.items.len) + param_offset;
|
||||
const param = llvm_func.getParam(llvm_arg_i);
|
||||
// It is possible for the calling convention to make the argument's by-reference nature
|
||||
// disagree with our canonical value for it, in which case we must dereference here.
|
||||
const need_deref = !param_ty.isPtrAtRuntime() and !isByRef(param_ty) and
|
||||
(param.typeOf().getTypeKind() == .Pointer);
|
||||
const loaded_param = if (!need_deref) param else l: {
|
||||
const load_inst = builder.buildLoad(param, "");
|
||||
load_inst.setAlignment(param_ty.abiAlignment(target));
|
||||
break :l load_inst;
|
||||
if (isByRef(param_ty)) {
|
||||
const alignment = param_ty.abiAlignment(target);
|
||||
const param_llvm_ty = param.typeOf();
|
||||
const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty);
|
||||
arg_ptr.setAlignment(alignment);
|
||||
const store_inst = builder.buildStore(param, arg_ptr);
|
||||
store_inst.setAlignment(alignment);
|
||||
try args.append(arg_ptr);
|
||||
} else {
|
||||
try args.append(param);
|
||||
}
|
||||
},
|
||||
.byref => {
|
||||
const param_ty = fn_info.param_types[it.zig_index - 1];
|
||||
const param = llvm_func.getParam(llvm_arg_i);
|
||||
llvm_arg_i += 1;
|
||||
|
||||
if (isByRef(param_ty)) {
|
||||
try args.append(param);
|
||||
} else {
|
||||
const alignment = param_ty.abiAlignment(target);
|
||||
const load_inst = builder.buildLoad(param, "");
|
||||
load_inst.setAlignment(alignment);
|
||||
try args.append(load_inst);
|
||||
}
|
||||
},
|
||||
.abi_sized_int => {
|
||||
const param_ty = fn_info.param_types[it.zig_index - 1];
|
||||
const param = llvm_func.getParam(llvm_arg_i);
|
||||
llvm_arg_i += 1;
|
||||
|
||||
const param_llvm_ty = try dg.llvmType(param_ty);
|
||||
const abi_size = @intCast(c_uint, param_ty.abiSize(target));
|
||||
const int_llvm_ty = dg.context.intType(abi_size * 8);
|
||||
const int_ptr_llvm_ty = int_llvm_ty.pointerType(0);
|
||||
const alignment = @maximum(
|
||||
param_ty.abiAlignment(target),
|
||||
dg.object.target_data.abiAlignmentOfType(int_llvm_ty),
|
||||
);
|
||||
const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty);
|
||||
arg_ptr.setAlignment(alignment);
|
||||
const casted_ptr = builder.buildBitCast(arg_ptr, int_ptr_llvm_ty, "");
|
||||
const store_inst = builder.buildStore(param, casted_ptr);
|
||||
store_inst.setAlignment(alignment);
|
||||
|
||||
if (isByRef(param_ty)) {
|
||||
try args.append(arg_ptr);
|
||||
} else {
|
||||
const load_inst = builder.buildLoad(arg_ptr, "");
|
||||
load_inst.setAlignment(alignment);
|
||||
try args.append(load_inst);
|
||||
}
|
||||
},
|
||||
.multiple_llvm_ints => {
|
||||
const param_ty = fn_info.param_types[it.zig_index - 1];
|
||||
const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len];
|
||||
const is_by_ref = isByRef(param_ty);
|
||||
switch (param_ty.zigTypeTag()) {
|
||||
.Struct => {
|
||||
const fields = param_ty.structFields().values();
|
||||
if (is_by_ref) {
|
||||
const param_llvm_ty = try dg.llvmType(param_ty);
|
||||
const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty);
|
||||
arg_ptr.setAlignment(param_ty.abiAlignment(target));
|
||||
|
||||
var field_i: u32 = 0;
|
||||
for (llvm_ints) |int_bits| {
|
||||
const param = llvm_func.getParam(llvm_arg_i);
|
||||
llvm_arg_i += 1;
|
||||
|
||||
const big_int_ty = dg.context.intType(int_bits);
|
||||
var bits_used: u16 = 0;
|
||||
while (bits_used < int_bits) {
|
||||
const field = fields[field_i];
|
||||
const field_abi_bits = @intCast(u16, field.ty.abiSize(target)) * 8;
|
||||
const field_int_ty = dg.context.intType(field_abi_bits);
|
||||
const shifted = if (bits_used == 0) param else s: {
|
||||
const shift_amt = big_int_ty.constInt(bits_used, .False);
|
||||
break :s builder.buildLShr(param, shift_amt, "");
|
||||
};
|
||||
const field_as_int = builder.buildTrunc(shifted, field_int_ty, "");
|
||||
var ty_buf: Type.Payload.Pointer = undefined;
|
||||
const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;
|
||||
const field_ptr = builder.buildStructGEP(arg_ptr, llvm_i, "");
|
||||
const field_alignment = field.normalAlignment(target);
|
||||
const casted_ptr = builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), "");
|
||||
const store_inst = builder.buildStore(field_as_int, casted_ptr);
|
||||
store_inst.setAlignment(field_alignment);
|
||||
|
||||
bits_used += field_abi_bits;
|
||||
field_i += 1;
|
||||
if (field_i >= fields.len) break;
|
||||
}
|
||||
if (field_i >= fields.len) break;
|
||||
}
|
||||
|
||||
try args.append(arg_ptr);
|
||||
} else {
|
||||
@panic("TODO: LLVM backend: implement C calling convention on x86_64 with byval struct parameter");
|
||||
}
|
||||
},
|
||||
.Union => {
|
||||
@panic("TODO: LLVM backend: implement C calling convention on x86_64 with union parameter");
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
};
|
||||
try args.append(loaded_param);
|
||||
}
|
||||
|
||||
var di_file: ?*llvm.DIFile = null;
|
||||
@ -2072,22 +2177,31 @@ pub const DeclGen = struct {
|
||||
}
|
||||
|
||||
// Set parameter attributes.
|
||||
var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing);
|
||||
for (fn_info.param_types) |param_ty| {
|
||||
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
|
||||
|
||||
if (isByRef(param_ty)) {
|
||||
dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull");
|
||||
// TODO readonly, noalias, align
|
||||
}
|
||||
llvm_param_i += 1;
|
||||
}
|
||||
|
||||
// TODO: more attributes. see codegen.cpp `make_fn_llvm_value`.
|
||||
if (fn_info.cc == .Naked) {
|
||||
dg.addFnAttr(llvm_fn, "naked");
|
||||
} else {
|
||||
llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target));
|
||||
switch (fn_info.cc) {
|
||||
.Unspecified, .Inline => {
|
||||
var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing);
|
||||
for (fn_info.param_types) |param_ty| {
|
||||
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
|
||||
|
||||
if (isByRef(param_ty)) {
|
||||
dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull");
|
||||
// TODO readonly, noalias, align
|
||||
}
|
||||
llvm_param_i += 1;
|
||||
}
|
||||
llvm_fn.setFunctionCallConv(.Fast);
|
||||
},
|
||||
.Naked => {
|
||||
dg.addFnAttr(llvm_fn, "naked");
|
||||
},
|
||||
.Async => {
|
||||
llvm_fn.setFunctionCallConv(.Fast);
|
||||
@panic("TODO: LLVM backend lower async function");
|
||||
},
|
||||
else => {
|
||||
llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target));
|
||||
},
|
||||
}
|
||||
|
||||
if (fn_info.alignment != 0) {
|
||||
@ -2518,42 +2632,7 @@ pub const DeclGen = struct {
|
||||
llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False);
|
||||
return llvm_union_ty;
|
||||
},
|
||||
.Fn => {
|
||||
const fn_info = t.fnInfo();
|
||||
const llvm_ret_ty = try lowerFnRetTy(dg, fn_info);
|
||||
|
||||
var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa);
|
||||
defer llvm_params.deinit();
|
||||
|
||||
if (firstParamSRet(fn_info, target)) {
|
||||
const llvm_sret_ty = try dg.llvmType(fn_info.return_type);
|
||||
try llvm_params.append(llvm_sret_ty.pointerType(0));
|
||||
}
|
||||
|
||||
if (fn_info.return_type.isError() and
|
||||
dg.module.comp.bin_file.options.error_return_tracing)
|
||||
{
|
||||
var ptr_ty_payload: Type.Payload.ElemType = .{
|
||||
.base = .{ .tag = .single_mut_pointer },
|
||||
.data = dg.object.getStackTraceType(),
|
||||
};
|
||||
const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
|
||||
try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, ptr_ty));
|
||||
}
|
||||
|
||||
for (fn_info.param_types) |param_ty| {
|
||||
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
|
||||
|
||||
try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, param_ty));
|
||||
}
|
||||
|
||||
return llvm.functionType(
|
||||
llvm_ret_ty,
|
||||
llvm_params.items.ptr,
|
||||
@intCast(c_uint, llvm_params.items.len),
|
||||
llvm.Bool.fromBool(fn_info.is_var_args),
|
||||
);
|
||||
},
|
||||
.Fn => return llvmTypeFn(dg, t),
|
||||
.ComptimeInt => unreachable,
|
||||
.ComptimeFloat => unreachable,
|
||||
.Type => unreachable,
|
||||
@ -2568,6 +2647,63 @@ pub const DeclGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn llvmTypeFn(dg: *DeclGen, fn_ty: Type) Allocator.Error!*const llvm.Type {
|
||||
const target = dg.module.getTarget();
|
||||
const fn_info = fn_ty.fnInfo();
|
||||
const llvm_ret_ty = try lowerFnRetTy(dg, fn_info);
|
||||
|
||||
var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa);
|
||||
defer llvm_params.deinit();
|
||||
|
||||
if (firstParamSRet(fn_info, target)) {
|
||||
const llvm_sret_ty = try dg.llvmType(fn_info.return_type);
|
||||
try llvm_params.append(llvm_sret_ty.pointerType(0));
|
||||
}
|
||||
|
||||
if (fn_info.return_type.isError() and
|
||||
dg.module.comp.bin_file.options.error_return_tracing)
|
||||
{
|
||||
var ptr_ty_payload: Type.Payload.ElemType = .{
|
||||
.base = .{ .tag = .single_mut_pointer },
|
||||
.data = dg.object.getStackTraceType(),
|
||||
};
|
||||
const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
|
||||
try llvm_params.append(try dg.llvmType(ptr_ty));
|
||||
}
|
||||
|
||||
var it = iterateParamTypes(dg, fn_info);
|
||||
while (it.next()) |lowering| switch (lowering) {
|
||||
.no_bits => continue,
|
||||
.byval => {
|
||||
const param_ty = fn_info.param_types[it.zig_index - 1];
|
||||
try llvm_params.append(try dg.llvmType(param_ty));
|
||||
},
|
||||
.byref => {
|
||||
const param_ty = fn_info.param_types[it.zig_index - 1];
|
||||
const raw_llvm_ty = try dg.llvmType(param_ty);
|
||||
try llvm_params.append(raw_llvm_ty.pointerType(0));
|
||||
},
|
||||
.abi_sized_int => {
|
||||
const param_ty = fn_info.param_types[it.zig_index - 1];
|
||||
const abi_size = @intCast(c_uint, param_ty.abiSize(target));
|
||||
try llvm_params.append(dg.context.intType(abi_size * 8));
|
||||
},
|
||||
.multiple_llvm_ints => {
|
||||
try llvm_params.ensureUnusedCapacity(it.llvm_types_len);
|
||||
for (it.llvm_types_buffer[0..it.llvm_types_len]) |int_bits| {
|
||||
llvm_params.appendAssumeCapacity(dg.context.intType(int_bits));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return llvm.functionType(
|
||||
llvm_ret_ty,
|
||||
llvm_params.items.ptr,
|
||||
@intCast(c_uint, llvm_params.items.len),
|
||||
llvm.Bool.fromBool(fn_info.is_var_args),
|
||||
);
|
||||
}
|
||||
|
||||
fn genTypedValue(dg: *DeclGen, tv: TypedValue) Error!*const llvm.Value {
|
||||
if (tv.val.isUndef()) {
|
||||
const llvm_type = try dg.llvmType(tv.ty);
|
||||
@ -3848,52 +3984,138 @@ pub const FuncGen = struct {
|
||||
try llvm_args.append(self.err_ret_trace.?);
|
||||
}
|
||||
|
||||
for (args) |arg| {
|
||||
const param_ty = self.air.typeOf(arg);
|
||||
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
|
||||
|
||||
const llvm_arg = try self.resolveInst(arg);
|
||||
const abi_llvm_ty = try lowerFnParamTy(self.dg, fn_info.cc, param_ty);
|
||||
const param_llvm_ty = llvm_arg.typeOf();
|
||||
if (abi_llvm_ty == param_llvm_ty) {
|
||||
try llvm_args.append(llvm_arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
// In this case the function param type is honoring the calling convention
|
||||
// by having a different LLVM type than the usual one. We solve this here
|
||||
// at the callsite by bitcasting a pointer to our canonical type, then
|
||||
// loading it if necessary.
|
||||
const alignment = param_ty.abiAlignment(target);
|
||||
const ptr_abi_ty = abi_llvm_ty.pointerType(0);
|
||||
|
||||
const casted_ptr = if (isByRef(param_ty))
|
||||
self.builder.buildBitCast(llvm_arg, ptr_abi_ty, "")
|
||||
else p: {
|
||||
const arg_ptr = self.buildAlloca(param_llvm_ty);
|
||||
arg_ptr.setAlignment(alignment);
|
||||
const store_inst = self.builder.buildStore(llvm_arg, arg_ptr);
|
||||
store_inst.setAlignment(alignment);
|
||||
|
||||
if (abi_llvm_ty.getTypeKind() == .Pointer) {
|
||||
// In this case, the calling convention wants a pointer, but
|
||||
// we have a value.
|
||||
if (arg_ptr.typeOf() == abi_llvm_ty) {
|
||||
try llvm_args.append(arg_ptr);
|
||||
continue;
|
||||
var it = iterateParamTypes(self.dg, fn_info);
|
||||
while (it.next()) |lowering| switch (lowering) {
|
||||
.no_bits => continue,
|
||||
.byval => {
|
||||
const arg = args[it.zig_index - 1];
|
||||
const param_ty = self.air.typeOf(arg);
|
||||
const llvm_arg = try self.resolveInst(arg);
|
||||
if (isByRef(param_ty)) {
|
||||
const alignment = param_ty.abiAlignment(target);
|
||||
const load_inst = self.builder.buildLoad(llvm_arg, "");
|
||||
load_inst.setAlignment(alignment);
|
||||
try llvm_args.append(load_inst);
|
||||
} else {
|
||||
if (param_ty.zigTypeTag() == .Pointer) {
|
||||
// We need a bitcast in case of two possibilities:
|
||||
// 1. The parameter type is a pointer to zero-sized type,
|
||||
// which is always lowered to an LLVM type of `*i8`.
|
||||
// 2. The argument is a global which does act as a pointer, however
|
||||
// a bitcast is needed in order for the LLVM types to match.
|
||||
const llvm_param_ty = try self.dg.llvmType(param_ty);
|
||||
const casted_ptr = self.builder.buildBitCast(llvm_arg, llvm_param_ty, "");
|
||||
try llvm_args.append(casted_ptr);
|
||||
} else {
|
||||
try llvm_args.append(llvm_arg);
|
||||
}
|
||||
const casted_ptr = self.builder.buildBitCast(arg_ptr, abi_llvm_ty, "");
|
||||
try llvm_args.append(casted_ptr);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
.byref => {
|
||||
const arg = args[it.zig_index - 1];
|
||||
const param_ty = self.air.typeOf(arg);
|
||||
const llvm_arg = try self.resolveInst(arg);
|
||||
if (isByRef(param_ty)) {
|
||||
try llvm_args.append(llvm_arg);
|
||||
} else {
|
||||
const alignment = param_ty.abiAlignment(target);
|
||||
const param_llvm_ty = llvm_arg.typeOf();
|
||||
const arg_ptr = self.buildAlloca(param_llvm_ty);
|
||||
arg_ptr.setAlignment(alignment);
|
||||
const store_inst = self.builder.buildStore(llvm_arg, arg_ptr);
|
||||
store_inst.setAlignment(alignment);
|
||||
try llvm_args.append(arg_ptr);
|
||||
}
|
||||
},
|
||||
.abi_sized_int => {
|
||||
const arg = args[it.zig_index - 1];
|
||||
const param_ty = self.air.typeOf(arg);
|
||||
const llvm_arg = try self.resolveInst(arg);
|
||||
const abi_size = @intCast(c_uint, param_ty.abiSize(target));
|
||||
const int_llvm_ty = self.dg.context.intType(abi_size * 8);
|
||||
const int_ptr_llvm_ty = int_llvm_ty.pointerType(0);
|
||||
|
||||
break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, "");
|
||||
};
|
||||
if (isByRef(param_ty)) {
|
||||
const alignment = param_ty.abiAlignment(target);
|
||||
const casted_ptr = self.builder.buildBitCast(llvm_arg, int_ptr_llvm_ty, "");
|
||||
const load_inst = self.builder.buildLoad(casted_ptr, "");
|
||||
load_inst.setAlignment(alignment);
|
||||
try llvm_args.append(load_inst);
|
||||
} else {
|
||||
// LLVM does not allow bitcasting structs so we must allocate
|
||||
// a local, bitcast its pointer, store, and then load.
|
||||
const alignment = @maximum(
|
||||
param_ty.abiAlignment(target),
|
||||
self.dg.object.target_data.abiAlignmentOfType(int_llvm_ty),
|
||||
);
|
||||
const int_ptr = self.buildAlloca(int_llvm_ty);
|
||||
int_ptr.setAlignment(alignment);
|
||||
const param_llvm_ty = try self.dg.llvmType(param_ty);
|
||||
const casted_ptr = self.builder.buildBitCast(int_ptr, param_llvm_ty.pointerType(0), "");
|
||||
const store_inst = self.builder.buildStore(llvm_arg, casted_ptr);
|
||||
store_inst.setAlignment(alignment);
|
||||
const load_inst = self.builder.buildLoad(int_ptr, "");
|
||||
load_inst.setAlignment(alignment);
|
||||
try llvm_args.append(load_inst);
|
||||
}
|
||||
},
|
||||
.multiple_llvm_ints => {
|
||||
const arg = args[it.zig_index - 1];
|
||||
const param_ty = self.air.typeOf(arg);
|
||||
const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len];
|
||||
const llvm_arg = try self.resolveInst(arg);
|
||||
const is_by_ref = isByRef(param_ty);
|
||||
try llvm_args.ensureUnusedCapacity(it.llvm_types_len);
|
||||
switch (param_ty.zigTypeTag()) {
|
||||
.Struct => {
|
||||
const fields = param_ty.structFields().values();
|
||||
var field_i: u32 = 0;
|
||||
for (llvm_ints) |int_bits| {
|
||||
const big_int_ty = self.dg.context.intType(int_bits);
|
||||
var int_arg: *const llvm.Value = undefined;
|
||||
var bits_used: u16 = 0;
|
||||
while (bits_used < int_bits) {
|
||||
const field = fields[field_i];
|
||||
var ty_buf: Type.Payload.Pointer = undefined;
|
||||
const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;
|
||||
const field_abi_bits = @intCast(u16, field.ty.abiSize(target)) * 8;
|
||||
const field_int_ty = self.dg.context.intType(field_abi_bits);
|
||||
const llvm_field = if (is_by_ref) f: {
|
||||
const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, "");
|
||||
const alignment = field.normalAlignment(target);
|
||||
const casted_ptr = self.builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), "");
|
||||
const load_inst = self.builder.buildLoad(casted_ptr, "");
|
||||
load_inst.setAlignment(alignment);
|
||||
break :f load_inst;
|
||||
} else f: {
|
||||
const llvm_field = self.builder.buildExtractValue(llvm_arg, llvm_i, "");
|
||||
break :f self.builder.buildBitCast(llvm_field, field_int_ty, "");
|
||||
};
|
||||
|
||||
const load_inst = self.builder.buildLoad(casted_ptr, "");
|
||||
load_inst.setAlignment(alignment);
|
||||
try llvm_args.append(load_inst);
|
||||
}
|
||||
const extended = self.builder.buildZExt(llvm_field, big_int_ty, "");
|
||||
if (bits_used == 0) {
|
||||
int_arg = extended;
|
||||
} else {
|
||||
const shift_amt = big_int_ty.constInt(bits_used, .False);
|
||||
const shifted = self.builder.buildShl(extended, shift_amt, "");
|
||||
int_arg = self.builder.buildOr(int_arg, shifted, "");
|
||||
}
|
||||
|
||||
bits_used += field_abi_bits;
|
||||
field_i += 1;
|
||||
if (field_i >= fields.len) break;
|
||||
}
|
||||
llvm_args.appendAssumeCapacity(int_arg);
|
||||
if (field_i >= fields.len) break;
|
||||
}
|
||||
},
|
||||
.Union => {
|
||||
return self.todo("airCall C calling convention on x86_64 with union argument ", .{});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const call = self.builder.buildCall(
|
||||
llvm_fn,
|
||||
@ -6489,24 +6711,7 @@ pub const FuncGen = struct {
|
||||
/// Use this instead of builder.buildAlloca, because this function makes sure to
|
||||
/// put the alloca instruction at the top of the function!
|
||||
fn buildAlloca(self: *FuncGen, llvm_ty: *const llvm.Type) *const llvm.Value {
|
||||
const prev_block = self.builder.getInsertBlock();
|
||||
const prev_debug_location = self.builder.getCurrentDebugLocation2();
|
||||
defer {
|
||||
self.builder.positionBuilderAtEnd(prev_block);
|
||||
if (self.di_scope != null) {
|
||||
self.builder.setCurrentDebugLocation2(prev_debug_location);
|
||||
}
|
||||
}
|
||||
|
||||
const entry_block = self.llvm_func.getFirstBasicBlock().?;
|
||||
if (entry_block.getFirstInstruction()) |first_inst| {
|
||||
self.builder.positionBuilder(entry_block, first_inst);
|
||||
} else {
|
||||
self.builder.positionBuilderAtEnd(entry_block);
|
||||
}
|
||||
self.builder.clearCurrentDebugLocation();
|
||||
|
||||
return self.builder.buildAlloca(llvm_ty, "");
|
||||
return buildAllocaInner(self.builder, self.llvm_func, self.di_scope != null, llvm_ty);
|
||||
}
|
||||
|
||||
fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
@ -8296,99 +8501,171 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.
|
||||
}
|
||||
}
|
||||
|
||||
fn lowerFnParamTy(dg: *DeclGen, cc: std.builtin.CallingConvention, ty: Type) !*const llvm.Type {
|
||||
assert(ty.hasRuntimeBitsIgnoreComptime());
|
||||
const target = dg.module.getTarget();
|
||||
switch (cc) {
|
||||
.Unspecified, .Inline => {
|
||||
const raw_llvm_ty = try dg.llvmType(ty);
|
||||
if (isByRef(ty)) {
|
||||
return raw_llvm_ty.pointerType(0);
|
||||
} else {
|
||||
return raw_llvm_ty;
|
||||
}
|
||||
},
|
||||
.C => {
|
||||
const is_scalar = switch (ty.zigTypeTag()) {
|
||||
.Void,
|
||||
.Bool,
|
||||
.NoReturn,
|
||||
.Int,
|
||||
.Float,
|
||||
.Pointer,
|
||||
.Optional,
|
||||
.ErrorSet,
|
||||
.Enum,
|
||||
.AnyFrame,
|
||||
.Vector,
|
||||
=> true,
|
||||
const ParamTypeIterator = struct {
|
||||
dg: *DeclGen,
|
||||
fn_info: Type.Payload.Function.Data,
|
||||
zig_index: u32,
|
||||
llvm_index: u32,
|
||||
target: std.Target,
|
||||
llvm_types_len: u32,
|
||||
llvm_types_buffer: [8]u16,
|
||||
|
||||
else => false,
|
||||
};
|
||||
switch (target.cpu.arch) {
|
||||
.mips, .mipsel => return dg.llvmType(ty),
|
||||
.x86_64 => switch (target.os.tag) {
|
||||
.windows => switch (x86_64_abi.classifyWindows(ty, target)) {
|
||||
.integer => {
|
||||
if (is_scalar) {
|
||||
return dg.llvmType(ty);
|
||||
} else {
|
||||
const abi_size = ty.abiSize(target);
|
||||
return dg.context.intType(@intCast(c_uint, abi_size * 8));
|
||||
}
|
||||
const Lowering = enum {
|
||||
no_bits,
|
||||
byval,
|
||||
byref,
|
||||
abi_sized_int,
|
||||
multiple_llvm_ints,
|
||||
};
|
||||
|
||||
fn next(it: *ParamTypeIterator) ?Lowering {
|
||||
if (it.zig_index >= it.fn_info.param_types.len) return null;
|
||||
|
||||
const ty = it.fn_info.param_types[it.zig_index];
|
||||
if (!ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
it.zig_index += 1;
|
||||
return .no_bits;
|
||||
}
|
||||
|
||||
switch (it.fn_info.cc) {
|
||||
.Unspecified, .Inline => {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
if (isByRef(ty)) {
|
||||
return .byref;
|
||||
} else {
|
||||
return .byval;
|
||||
}
|
||||
},
|
||||
.Async => {
|
||||
@panic("TODO implement async function lowering in the LLVM backend");
|
||||
},
|
||||
.C => {
|
||||
const is_scalar = switch (ty.zigTypeTag()) {
|
||||
.Void,
|
||||
.Bool,
|
||||
.NoReturn,
|
||||
.Int,
|
||||
.Float,
|
||||
.Pointer,
|
||||
.Optional,
|
||||
.ErrorSet,
|
||||
.Enum,
|
||||
.AnyFrame,
|
||||
.Vector,
|
||||
=> true,
|
||||
|
||||
else => false,
|
||||
};
|
||||
switch (it.target.cpu.arch) {
|
||||
.mips, .mipsel => {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
return .byval;
|
||||
},
|
||||
.x86_64 => switch (it.target.os.tag) {
|
||||
.windows => switch (x86_64_abi.classifyWindows(ty, it.target)) {
|
||||
.integer => {
|
||||
if (is_scalar) {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
return .byval;
|
||||
} else {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
return .abi_sized_int;
|
||||
}
|
||||
},
|
||||
.memory => {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
return .byref;
|
||||
},
|
||||
.sse => {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
return .byval;
|
||||
},
|
||||
else => unreachable,
|
||||
},
|
||||
.memory => return (try dg.llvmType(ty)).pointerType(0),
|
||||
.sse => return dg.llvmType(ty),
|
||||
else => unreachable,
|
||||
},
|
||||
else => {
|
||||
if (is_scalar) {
|
||||
return dg.llvmType(ty);
|
||||
}
|
||||
const classes = x86_64_abi.classifySystemV(ty, target);
|
||||
if (classes[0] == .memory) {
|
||||
return (try dg.llvmType(ty)).pointerType(0);
|
||||
}
|
||||
var llvm_types_buffer: [8]*const llvm.Type = undefined;
|
||||
var llvm_types_index: u32 = 0;
|
||||
for (classes) |class| {
|
||||
switch (class) {
|
||||
.integer => {
|
||||
llvm_types_buffer[llvm_types_index] = dg.context.intType(64);
|
||||
llvm_types_index += 1;
|
||||
},
|
||||
.sse => {
|
||||
@panic("TODO");
|
||||
},
|
||||
.sseup => {
|
||||
@panic("TODO");
|
||||
},
|
||||
.x87 => {
|
||||
@panic("TODO");
|
||||
},
|
||||
.x87up => {
|
||||
@panic("TODO");
|
||||
},
|
||||
.complex_x87 => {
|
||||
@panic("TODO");
|
||||
},
|
||||
.memory => unreachable, // handled above
|
||||
.none => break,
|
||||
else => {
|
||||
if (is_scalar) {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
return .byval;
|
||||
}
|
||||
}
|
||||
if (classes[0] == .integer and classes[1] == .none) {
|
||||
const abi_size = ty.abiSize(target);
|
||||
return dg.context.intType(@intCast(c_uint, abi_size * 8));
|
||||
}
|
||||
return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False);
|
||||
const classes = x86_64_abi.classifySystemV(ty, it.target);
|
||||
if (classes[0] == .memory) {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
return .byref;
|
||||
}
|
||||
var llvm_types_buffer: [8]u16 = undefined;
|
||||
var llvm_types_index: u32 = 0;
|
||||
for (classes) |class| {
|
||||
switch (class) {
|
||||
.integer => {
|
||||
llvm_types_buffer[llvm_types_index] = 64;
|
||||
llvm_types_index += 1;
|
||||
},
|
||||
.sse => {
|
||||
@panic("TODO");
|
||||
},
|
||||
.sseup => {
|
||||
@panic("TODO");
|
||||
},
|
||||
.x87 => {
|
||||
@panic("TODO");
|
||||
},
|
||||
.x87up => {
|
||||
@panic("TODO");
|
||||
},
|
||||
.complex_x87 => {
|
||||
@panic("TODO");
|
||||
},
|
||||
.memory => unreachable, // handled above
|
||||
.none => break,
|
||||
}
|
||||
}
|
||||
if (classes[0] == .integer and classes[1] == .none) {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
return .abi_sized_int;
|
||||
}
|
||||
it.llvm_types_buffer = llvm_types_buffer;
|
||||
it.llvm_types_len = llvm_types_index;
|
||||
it.llvm_index += llvm_types_index;
|
||||
it.zig_index += 1;
|
||||
return .multiple_llvm_ints;
|
||||
},
|
||||
},
|
||||
},
|
||||
// TODO investigate C ABI for other architectures
|
||||
else => return dg.llvmType(ty),
|
||||
}
|
||||
},
|
||||
else => return dg.llvmType(ty),
|
||||
// TODO investigate C ABI for other architectures
|
||||
else => {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
return .byval;
|
||||
},
|
||||
}
|
||||
},
|
||||
else => {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
return .byval;
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn iterateParamTypes(dg: *DeclGen, fn_info: Type.Payload.Function.Data) ParamTypeIterator {
|
||||
return .{
|
||||
.dg = dg,
|
||||
.fn_info = fn_info,
|
||||
.zig_index = 0,
|
||||
.llvm_index = 0,
|
||||
.target = dg.module.getTarget(),
|
||||
.llvm_types_buffer = undefined,
|
||||
.llvm_types_len = 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn isByRef(ty: Type) bool {
|
||||
@ -8549,3 +8826,29 @@ fn compilerRtIntBits(bits: u16) u16 {
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
fn buildAllocaInner(
|
||||
builder: *const llvm.Builder,
|
||||
llvm_func: *const llvm.Value,
|
||||
di_scope_non_null: bool,
|
||||
llvm_ty: *const llvm.Type,
|
||||
) *const llvm.Value {
|
||||
const prev_block = builder.getInsertBlock();
|
||||
const prev_debug_location = builder.getCurrentDebugLocation2();
|
||||
defer {
|
||||
builder.positionBuilderAtEnd(prev_block);
|
||||
if (di_scope_non_null) {
|
||||
builder.setCurrentDebugLocation2(prev_debug_location);
|
||||
}
|
||||
}
|
||||
|
||||
const entry_block = llvm_func.getFirstBasicBlock().?;
|
||||
if (entry_block.getFirstInstruction()) |first_inst| {
|
||||
builder.positionBuilder(entry_block, first_inst);
|
||||
} else {
|
||||
builder.positionBuilderAtEnd(entry_block);
|
||||
}
|
||||
builder.clearCurrentDebugLocation();
|
||||
|
||||
return builder.buildAlloca(llvm_ty, "");
|
||||
}
|
||||
|
||||
@ -1019,6 +1019,9 @@ pub const TargetMachine = opaque {
|
||||
pub const TargetData = opaque {
|
||||
pub const dispose = LLVMDisposeTargetData;
|
||||
extern fn LLVMDisposeTargetData(*const TargetData) void;
|
||||
|
||||
pub const abiAlignmentOfType = LLVMABIAlignmentOfType;
|
||||
extern fn LLVMABIAlignmentOfType(TD: *const TargetData, Ty: *const Type) c_uint;
|
||||
};
|
||||
|
||||
pub const CodeModel = enum(c_int) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user