@@ -4904,9 +4904,9 @@ pub const FuncGen = struct {
4904
4904
.set_err_return_trace => try self.airSetErrReturnTrace(inst),
4905
4905
.save_err_return_trace_index => try self.airSaveErrReturnTraceIndex(inst),
4906
4906
4907
- .wrap_optional => try self.airWrapOptional(inst ),
4908
- .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst ),
4909
- .wrap_errunion_err => try self.airWrapErrUnionErr(inst ),
4907
+ .wrap_optional => try self.airWrapOptional(body[i..] ),
4908
+ .wrap_errunion_payload => try self.airWrapErrUnionPayload(body[i..] ),
4909
+ .wrap_errunion_err => try self.airWrapErrUnionErr(body[i..] ),
4910
4910
4911
4911
.wasm_memory_size => try self.airWasmMemorySize(inst),
4912
4912
.wasm_memory_grow => try self.airWasmMemoryGrow(inst),
@@ -5299,6 +5299,16 @@ pub const FuncGen = struct {
5299
5299
if (self.ret_ptr != .none) {
5300
5300
const operand = try self.resolveInst(un_op);
5301
5301
const ptr_ty = try mod.singleMutPtrType(ret_ty);
5302
+
5303
+ const unwrapped_operand = operand.unwrap();
5304
+ const unwrapped_ret = self.ret_ptr.unwrap();
5305
+
5306
+ // Return value was stored previously
5307
+ if (unwrapped_operand == .instruction and unwrapped_ret == .instruction and unwrapped_operand.instruction == unwrapped_ret.instruction) {
5308
+ _ = try self.wip.retVoid();
5309
+ return .none;
5310
+ }
5311
+
5302
5312
try self.store(self.ret_ptr, ptr_ty, operand, .none);
5303
5313
_ = try self.wip.retVoid();
5304
5314
return .none;
@@ -7179,9 +7189,33 @@ pub const FuncGen = struct {
7179
7189
return self.load(field_ptr, field_ptr_ty);
7180
7190
}
7181
7191
7182
- fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
7192
+ /// As an optimization, we want to avoid unnecessary copies of
7193
+ /// error union/optional types when returning from a function.
7194
+ /// Here, we scan forward in the current block, looking to see
7195
+ /// if the next instruction is a return (ignoring debug instructions).
7196
+ ///
7197
+ /// The first instruction of `body_tail` is a wrap instruction.
7198
+ fn isNextRet(
7199
+ self: *FuncGen,
7200
+ body_tail: []const Air.Inst.Index,
7201
+ ) bool {
7202
+ const air_tags = self.air.instructions.items(.tag);
7203
+ for (body_tail[1..]) |body_inst| {
7204
+ switch (air_tags[body_inst]) {
7205
+ .ret => return true,
7206
+ .dbg_block_begin, .dbg_stmt => continue,
7207
+ else => return false,
7208
+ }
7209
+ }
7210
+ // The only way to get here is to hit the end of a loop instruction
7211
+ // (implicit repeat).
7212
+ return false;
7213
+ }
7214
+
7215
+ fn airWrapOptional(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value {
7183
7216
const o = self.dg.object;
7184
7217
const mod = o.module;
7218
+ const inst = body_tail[0];
7185
7219
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
7186
7220
const payload_ty = self.typeOf(ty_op.operand);
7187
7221
const non_null_bit = try o.builder.intValue(.i8, 1);
@@ -7192,8 +7226,15 @@ pub const FuncGen = struct {
7192
7226
if (optional_ty.optionalReprIsPayload(mod)) return operand;
7193
7227
const llvm_optional_ty = try o.lowerType(optional_ty);
7194
7228
if (isByRef(optional_ty, mod)) {
7195
- const alignment = optional_ty.abiAlignment(mod).toLlvm();
7196
- const optional_ptr = try self.buildAlloca(llvm_optional_ty, alignment);
7229
+ const directReturn = self.isNextRet(body_tail);
7230
+ const optional_ptr = if (directReturn)
7231
+ self.ret_ptr
7232
+ else brk: {
7233
+ const alignment = optional_ty.abiAlignment(mod).toLlvm();
7234
+ const optional_ptr = try self.buildAlloca(llvm_optional_ty, alignment);
7235
+ break :brk optional_ptr;
7236
+ };
7237
+
7197
7238
const payload_ptr = try self.wip.gepStruct(llvm_optional_ty, optional_ptr, 0, "");
7198
7239
const payload_ptr_ty = try mod.singleMutPtrType(payload_ty);
7199
7240
try self.store(payload_ptr, payload_ptr_ty, operand, .none);
@@ -7204,9 +7245,10 @@ pub const FuncGen = struct {
7204
7245
return self.wip.buildAggregate(llvm_optional_ty, &.{ operand, non_null_bit }, "");
7205
7246
}
7206
7247
7207
- fn airWrapErrUnionPayload(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
7248
+ fn airWrapErrUnionPayload(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value {
7208
7249
const o = self.dg.object;
7209
7250
const mod = o.module;
7251
+ const inst = body_tail[0];
7210
7252
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
7211
7253
const err_un_ty = self.typeOfIndex(inst);
7212
7254
const operand = try self.resolveInst(ty_op.operand);
@@ -7220,8 +7262,15 @@ pub const FuncGen = struct {
7220
7262
const payload_offset = errUnionPayloadOffset(payload_ty, mod);
7221
7263
const error_offset = errUnionErrorOffset(payload_ty, mod);
7222
7264
if (isByRef(err_un_ty, mod)) {
7223
- const alignment = err_un_ty.abiAlignment(mod).toLlvm();
7224
- const result_ptr = try self.buildAlloca(err_un_llvm_ty, alignment);
7265
+ const directReturn = self.isNextRet(body_tail);
7266
+ const result_ptr = if (directReturn)
7267
+ self.ret_ptr
7268
+ else brk: {
7269
+ const alignment = err_un_ty.abiAlignment(mod).toLlvm();
7270
+ const result_ptr = try self.buildAlloca(err_un_llvm_ty, alignment);
7271
+ break :brk result_ptr;
7272
+ };
7273
+
7225
7274
const err_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, error_offset, "");
7226
7275
const error_alignment = Type.err_int.abiAlignment(mod).toLlvm();
7227
7276
_ = try self.wip.store(.normal, ok_err_code, err_ptr, error_alignment);
@@ -7236,9 +7285,10 @@ pub const FuncGen = struct {
7236
7285
return self.wip.buildAggregate(err_un_llvm_ty, &fields, "");
7237
7286
}
7238
7287
7239
- fn airWrapErrUnionErr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
7288
+ fn airWrapErrUnionErr(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value {
7240
7289
const o = self.dg.object;
7241
7290
const mod = o.module;
7291
+ const inst = body_tail[0];
7242
7292
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
7243
7293
const err_un_ty = self.typeOfIndex(inst);
7244
7294
const payload_ty = err_un_ty.errorUnionPayload(mod);
@@ -7249,8 +7299,15 @@ pub const FuncGen = struct {
7249
7299
const payload_offset = errUnionPayloadOffset(payload_ty, mod);
7250
7300
const error_offset = errUnionErrorOffset(payload_ty, mod);
7251
7301
if (isByRef(err_un_ty, mod)) {
7252
- const alignment = err_un_ty.abiAlignment(mod).toLlvm();
7253
- const result_ptr = try self.buildAlloca(err_un_llvm_ty, alignment);
7302
+ const directReturn = self.isNextRet(body_tail);
7303
+ const result_ptr = if (directReturn)
7304
+ self.ret_ptr
7305
+ else brk: {
7306
+ const alignment = err_un_ty.abiAlignment(mod).toLlvm();
7307
+ const result_ptr = try self.buildAlloca(err_un_llvm_ty, alignment);
7308
+ break :brk result_ptr;
7309
+ };
7310
+
7254
7311
const err_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, error_offset, "");
7255
7312
const error_alignment = Type.err_int.abiAlignment(mod).toLlvm();
7256
7313
_ = try self.wip.store(.normal, operand, err_ptr, error_alignment);
0 commit comments