authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-01 13:30:25-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-01 14:58:37-07:00
log1b194931b0df08db0f38a284bb10b89cc00a8817
tree9b6c1f4a7f265e36f02dc7a0c03780f22ddd1713
parent18e42661dc9b8199311ee086b24ef5c85cf4708f

LLVM: fix when sret and isByRef ret_ty disagree

This can happen functions use the C ABI.

2 files changed, 56 insertions(+), 27 deletions(-)

src/codegen/llvm.zig+53-25
...@@ -450,10 +450,22 @@ pub const Object = struct {...@@ -450,10 +450,22 @@ pub const Object = struct {
450 DeclGen.removeFnAttr(llvm_func, "cold");450 DeclGen.removeFnAttr(llvm_func, "cold");
451 }451 }
452452
453 // Remove all the basic blocks of a function in order to start over, generating
454 // LLVM IR from an empty function body.
455 while (llvm_func.getFirstBasicBlock()) |bb| {
456 bb.deleteBasicBlock();
457 }
458
459 const builder = dg.context.createBuilder();
460
461 const entry_block = dg.context.appendBasicBlock(llvm_func, "Entry");
462 builder.positionBuilderAtEnd(entry_block);
463
453 // This gets the LLVM values from the function and stores them in `dg.args`.464 // This gets the LLVM values from the function and stores them in `dg.args`.
454 const fn_info = decl.ty.fnInfo();465 const fn_info = decl.ty.fnInfo();
455 const ret_ty_by_ref = isByRef(fn_info.return_type);466 const target = dg.module.getTarget();
456 const ret_ptr = if (ret_ty_by_ref) llvm_func.getParam(0) else null;467 const sret = firstParamSRet(fn_info, target);
468 const ret_ptr = if (sret) llvm_func.getParam(0) else null;
457469
458 var args = std.ArrayList(*const llvm.Value).init(dg.gpa);470 var args = std.ArrayList(*const llvm.Value).init(dg.gpa);
459 defer args.deinit();471 defer args.deinit();
...@@ -466,17 +478,6 @@ pub const Object = struct {...@@ -466,17 +478,6 @@ pub const Object = struct {
466 try args.append(llvm_func.getParam(llvm_arg_i));478 try args.append(llvm_func.getParam(llvm_arg_i));
467 }479 }
468480
469 // Remove all the basic blocks of a function in order to start over, generating
470 // LLVM IR from an empty function body.
471 while (llvm_func.getFirstBasicBlock()) |bb| {
472 bb.deleteBasicBlock();
473 }
474
475 const builder = dg.context.createBuilder();
476
477 const entry_block = dg.context.appendBasicBlock(llvm_func, "Entry");
478 builder.positionBuilderAtEnd(entry_block);
479
480 var fg: FuncGen = .{481 var fg: FuncGen = .{
481 .gpa = dg.gpa,482 .gpa = dg.gpa,
482 .air = air,483 .air = air,
...@@ -485,7 +486,7 @@ pub const Object = struct {...@@ -485,7 +486,7 @@ pub const Object = struct {
485 .dg = &dg,486 .dg = &dg,
486 .builder = builder,487 .builder = builder,
487 .ret_ptr = ret_ptr,488 .ret_ptr = ret_ptr,
488 .args = args.toOwnedSlice(),489 .args = args.items,
489 .arg_index = 0,490 .arg_index = 0,
490 .func_inst_table = .{},491 .func_inst_table = .{},
491 .llvm_func = llvm_func,492 .llvm_func = llvm_func,
...@@ -1977,20 +1978,22 @@ pub const FuncGen = struct {...@@ -1977,20 +1978,22 @@ pub const FuncGen = struct {
1977 air: Air,1978 air: Air,
1978 liveness: Liveness,1979 liveness: Liveness,
1979 context: *const llvm.Context,1980 context: *const llvm.Context,
1980
1981 builder: *const llvm.Builder,1981 builder: *const llvm.Builder,
19821982
1983 /// This stores the LLVM values used in a function, such that they can be referred to1983 /// This stores the LLVM values used in a function, such that they can be referred to
1984 /// in other instructions. This table is cleared before every function is generated.1984 /// in other instructions. This table is cleared before every function is generated.
1985 func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Ref, *const llvm.Value),1985 func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Ref, *const llvm.Value),
19861986
1987 /// If the return type isByRef, this is the result pointer. Otherwise null.1987 /// If the return type is sret, this is the result pointer. Otherwise null.
1988 /// Note that this can disagree with isByRef for the return type in the case
1989 /// of C ABI functions.
1988 ret_ptr: ?*const llvm.Value,1990 ret_ptr: ?*const llvm.Value,
1989 /// These fields are used to refer to the LLVM value of the function parameters1991 /// These fields are used to refer to the LLVM value of the function parameters
1990 /// in an Arg instruction.1992 /// in an Arg instruction.
1991 /// This list may be shorter than the list according to the zig type system;1993 /// This list may be shorter than the list according to the zig type system;
1992 /// it omits 0-bit types.1994 /// it omits 0-bit types. If the function uses sret as the first parameter,
1993 args: []*const llvm.Value,1995 /// this slice does not include it.
1996 args: []const *const llvm.Value,
1994 arg_index: usize,1997 arg_index: usize,
19951998
1996 llvm_func: *const llvm.Value,1999 llvm_func: *const llvm.Value,
...@@ -2010,7 +2013,6 @@ pub const FuncGen = struct {...@@ -2010,7 +2013,6 @@ pub const FuncGen = struct {
2010 fn deinit(self: *FuncGen) void {2013 fn deinit(self: *FuncGen) void {
2011 self.builder.dispose();2014 self.builder.dispose();
2012 self.func_inst_table.deinit(self.gpa);2015 self.func_inst_table.deinit(self.gpa);
2013 self.gpa.free(self.args);
2014 self.blocks.deinit(self.gpa);2016 self.blocks.deinit(self.gpa);
2015 }2017 }
20162018
...@@ -2283,12 +2285,35 @@ pub const FuncGen = struct {...@@ -2283,12 +2285,35 @@ pub const FuncGen = struct {
2283 if (return_type.isNoReturn()) {2285 if (return_type.isNoReturn()) {
2284 _ = self.builder.buildUnreachable();2286 _ = self.builder.buildUnreachable();
2285 return null;2287 return null;
2286 } else if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBits()) {2288 }
2289
2290 if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBits()) {
2287 return null;2291 return null;
2288 } else if (sret) {2292 }
2293
2294 if (ret_ptr) |rp| {
2289 const llvm_ret_ty = try self.dg.llvmType(return_type);2295 const llvm_ret_ty = try self.dg.llvmType(return_type);
2290 call.setCallSret(llvm_ret_ty);2296 call.setCallSret(llvm_ret_ty);
2291 return ret_ptr;2297 if (isByRef(return_type)) {
2298 return rp;
2299 } else {
2300 // our by-ref status disagrees with sret so we must load.
2301 const loaded = self.builder.buildLoad(rp, "");
2302 loaded.setAlignment(return_type.abiAlignment(target));
2303 return loaded;
2304 }
2305 }
2306
2307 if (isByRef(return_type)) {
2308 // our by-ref status disagrees with sret so we must allocate, store,
2309 // and return the allocation pointer.
2310 const llvm_ret_ty = try self.dg.llvmType(return_type);
2311 const rp = self.buildAlloca(llvm_ret_ty);
2312 const alignment = return_type.abiAlignment(target);
2313 rp.setAlignment(alignment);
2314 const store_inst = self.builder.buildStore(call, rp);
2315 store_inst.setAlignment(alignment);
2316 return rp;
2292 } else {2317 } else {
2293 return call;2318 return call;
2294 }2319 }
...@@ -2321,12 +2346,14 @@ pub const FuncGen = struct {...@@ -2321,12 +2346,14 @@ pub const FuncGen = struct {
2321 const un_op = self.air.instructions.items(.data)[inst].un_op;2346 const un_op = self.air.instructions.items(.data)[inst].un_op;
2322 const ptr_ty = self.air.typeOf(un_op);2347 const ptr_ty = self.air.typeOf(un_op);
2323 const ret_ty = ptr_ty.childType();2348 const ret_ty = ptr_ty.childType();
2324 if (!ret_ty.hasRuntimeBits() or isByRef(ret_ty)) {2349 if (!ret_ty.hasRuntimeBits() or self.ret_ptr != null) {
2325 _ = self.builder.buildRetVoid();2350 _ = self.builder.buildRetVoid();
2326 return null;2351 return null;
2327 }2352 }
2353 const target = self.dg.module.getTarget();
2328 const ptr = try self.resolveInst(un_op);2354 const ptr = try self.resolveInst(un_op);
2329 const loaded = self.builder.buildLoad(ptr, "");2355 const loaded = self.builder.buildLoad(ptr, "");
2356 loaded.setAlignment(ret_ty.abiAlignment(target));
2330 _ = self.builder.buildRet(loaded);2357 _ = self.builder.buildRet(loaded);
2331 return null;2358 return null;
2332 }2359 }
...@@ -5456,11 +5483,12 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool...@@ -5456,11 +5483,12 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
5456 .C => {},5483 .C => {},
5457 else => return false,5484 else => return false,
5458 }5485 }
5486 const x86_64_abi = @import("../arch/x86_64/abi.zig");
5459 switch (target.cpu.arch) {5487 switch (target.cpu.arch) {
5460 .mips, .mipsel => return false,5488 .mips, .mipsel => return false,
5461 .x86_64 => switch (target.os.tag) {5489 .x86_64 => switch (target.os.tag) {
5462 .windows => return @import("../arch/x86_64/abi.zig").classifyWindows(fn_info.return_type, target) == .memory,5490 .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,
5463 else => return @import("../arch/x86_64/abi.zig").classifySystemV(fn_info.return_type, target)[0] == .memory,5491 else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory,
5464 },5492 },
5465 else => return false, // TODO investigate C ABI for other architectures5493 else => return false, // TODO investigate C ABI for other architectures
5466 }5494 }
test/behavior/struct.zig+3-2
...@@ -833,12 +833,13 @@ test "packed struct with fp fields" {...@@ -833,12 +833,13 @@ test "packed struct with fp fields" {
833}833}
834834
835test "fn with C calling convention returns struct by value" {835test "fn with C calling convention returns struct by value" {
836 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO836 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
837 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
837838
838 const S = struct {839 const S = struct {
839 fn entry() !void {840 fn entry() !void {
840 var x = makeBar(10);841 var x = makeBar(10);
841 try expectEqual(@as(i32, 10), x.handle);842 try expect(@as(i32, 10) == x.handle);
842 }843 }
843844
844 const ExternBar = extern struct {845 const ExternBar = extern struct {