| ... | ... | @@ -795,7 +795,7 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std. |
| 795 | 795 | var returns = std.ArrayList(wasm.Valtype).init(gpa); |
| 796 | 796 | defer returns.deinit(); |
| 797 | 797 | |
| 798 | | if (firstParamSRet(fn_info, target)) { |
| 798 | if (firstParamSRet(fn_info.cc, fn_info.return_type, target)) { |
| 799 | 799 | try params.append(.i32); // memory address is always a 32-bit handle |
| 800 | 800 | } else if (fn_info.return_type.hasRuntimeBitsIgnoreComptime()) { |
| 801 | 801 | if (fn_info.cc == .C) { |
| ... | ... | @@ -993,7 +993,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu |
| 993 | 993 | |
| 994 | 994 | // Check if we store the result as a pointer to the stack rather than |
| 995 | 995 | // by value |
| 996 | | if (firstParamSRet(fn_ty.fnInfo(), self.target)) { |
| 996 | const fn_info = fn_ty.fnInfo(); |
| 997 | if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) { |
| 997 | 998 | // the sret arg will be passed as first argument, therefore we |
| 998 | 999 | // set the `return_value` before allocating locals for regular args. |
| 999 | 1000 | result.return_value = .{ .local = self.local_index }; |
| ... | ... | @@ -1027,11 +1028,11 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu |
| 1027 | 1028 | return result; |
| 1028 | 1029 | } |
| 1029 | 1030 | |
| 1030 | | fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { |
| 1031 | | switch (fn_info.cc) { |
| 1032 | | .Unspecified, .Inline => return isByRef(fn_info.return_type, target), |
| 1031 | fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, target: std.Target) bool { |
| 1032 | switch (cc) { |
| 1033 | .Unspecified, .Inline => return isByRef(return_type, target), |
| 1033 | 1034 | .C => { |
| 1034 | | const ty_classes = abi.classifyType(fn_info.return_type, target); |
| 1035 | const ty_classes = abi.classifyType(return_type, target); |
| 1035 | 1036 | if (ty_classes[0] == .indirect) return true; |
| 1036 | 1037 | if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true; |
| 1037 | 1038 | return false; |
| ... | ... | @@ -1678,7 +1679,8 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1678 | 1679 | return self.allocStack(Type.usize); // create pointer to void |
| 1679 | 1680 | } |
| 1680 | 1681 | |
| 1681 | | if (firstParamSRet(self.decl.ty.fnInfo(), self.target)) { |
| 1682 | const fn_info = self.decl.ty.fnInfo(); |
| 1683 | if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) { |
| 1682 | 1684 | return self.return_value; |
| 1683 | 1685 | } |
| 1684 | 1686 | |
| ... | ... | @@ -1697,7 +1699,8 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1697 | 1699 | } |
| 1698 | 1700 | } |
| 1699 | 1701 | |
| 1700 | | if (!firstParamSRet(self.decl.ty.fnInfo(), self.target)) { |
| 1702 | const fn_info = self.decl.ty.fnInfo(); |
| 1703 | if (!firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) { |
| 1701 | 1704 | const result = try self.load(operand, ret_ty, 0); |
| 1702 | 1705 | try self.emitWValue(result); |
| 1703 | 1706 | } |
| ... | ... | @@ -1720,7 +1723,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1720 | 1723 | else => unreachable, |
| 1721 | 1724 | }; |
| 1722 | 1725 | const ret_ty = fn_ty.fnReturnType(); |
| 1723 | | const first_param_sret = firstParamSRet(fn_ty.fnInfo(), self.target); |
| 1726 | const fn_info = fn_ty.fnInfo(); |
| 1727 | const first_param_sret = firstParamSRet(fn_info.cc, fn_info.return_type, self.target); |
| 1724 | 1728 | |
| 1725 | 1729 | const callee: ?*Decl = blk: { |
| 1726 | 1730 | const func_val = self.air.value(pl_op.operand) orelse break :blk null; |
| ... | ... | @@ -5091,3 +5095,65 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 5091 | 5095 | return shift_result; |
| 5092 | 5096 | } |
| 5093 | 5097 | } |
| 5098 | |
| 5099 | /// Calls a compiler-rt intrinsic by creating an undefined symbol, |
| 5100 | /// then lowering the arguments and calling the symbol as a function call. |
| 5101 | /// This function call assumes the C-ABI. |
| 5102 | fn callIntrinsic( |
| 5103 | self: *Self, |
| 5104 | name: []const u8, |
| 5105 | param_types: []const Type, |
| 5106 | return_type: Type, |
| 5107 | args: []const WValue, |
| 5108 | ) InnerError!WValue { |
| 5109 | assert(param_types.len == args.len); |
| 5110 | const symbol_index = @intCast(u32, try self.bin_file.getIntrinsicSymbol(name)); |
| 5111 | var pt_tmp = try self.gpa.dupe(Type, param_types); |
| 5112 | defer self.gpa.free(pt_tmp); |
| 5113 | |
| 5114 | // TODO: have genFunctype accept individual params so we don't, |
| 5115 | // need to initialize a fake Fn.Data instance. |
| 5116 | const func_type = try genFunctype(self.base.allocator, .{ |
| 5117 | .param_types = pt_tmp, |
| 5118 | .comptime_params = undefined, |
| 5119 | .return_type = return_type, |
| 5120 | .alignment = 0, |
| 5121 | .cc = .C, |
| 5122 | .is_var_args = false, |
| 5123 | .is_generic = false, |
| 5124 | }, self.target); |
| 5125 | defer func_type.deinit(self.base.allocator); |
| 5126 | const func_type_index = try self.bin_file.putOrGetFuncType(func_type); |
| 5127 | try self.bin_file.addOrUpdateImport(symbol_index, func_type_index); |
| 5128 | |
| 5129 | const want_sret_param = firstParamSRet(.C, return_type, self.target); |
| 5130 | // if we want return as first param, we allocate a pointer to stack, |
| 5131 | // and emit it as our first argument |
| 5132 | const sret = if (want_sret_param) blk: { |
| 5133 | const sret_local = try self.allocStack(return_type); |
| 5134 | try self.lowerToStack(sret_local); |
| 5135 | break :blk sret_local; |
| 5136 | } else WValue{ .none = {} }; |
| 5137 | |
| 5138 | // Lower all arguments to the stack before we call our function |
| 5139 | for (args) |arg, arg_i| { |
| 5140 | assert(param_types[arg_i].hasRuntimeBitsIgnoreComptime()); |
| 5141 | try self.lowerArg(.C, param_types[arg_i], arg); |
| 5142 | } |
| 5143 | |
| 5144 | // Actually call our intrinsic |
| 5145 | try self.addLabel(.call, symbol_index); |
| 5146 | |
| 5147 | if (!return_type.hasRuntimeBitsIgnoreComptime()) { |
| 5148 | return WValue.none; |
| 5149 | } else if (return_type.isNoReturn()) { |
| 5150 | try self.addTag(.@"unreachable"); |
| 5151 | return WValue.none; |
| 5152 | } else if (want_sret_param) { |
| 5153 | return sret; |
| 5154 | } else { |
| 5155 | const result_local = try self.allocLocal(return_type); |
| 5156 | try self.addLabel(.local_set, result_local.local); |
| 5157 | return result_local; |
| 5158 | } |
| 5159 | } |