| ... | @@ -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 | } |
| 452 | | 452 | |
| | 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; |
| 457 | | 469 | |
| 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 | } |
| 468 | | 480 | |
| 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, |
| 1982 | | 1982 | |
| 1983 | /// This stores the LLVM values used in a function, such that they can be referred to | 1983 | /// 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), |
| 1986 | | 1986 | |
| 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 parameters | 1991 | /// 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, |
| 1995 | | 1998 | |
| 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 | } |
| 2016 | | 2018 | |
| ... | @@ -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 architectures | 5493 | else => return false, // TODO investigate C ABI for other architectures |
| 5466 | } | 5494 | } |