| ... | ... | @@ -417,22 +417,24 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 417 | 417 | .f64 => return .f64_neg, |
| 418 | 418 | }, |
| 419 | 419 | .ceil => switch (args.valtype1.?) { |
| 420 | | .i32, .i64 => unreachable, |
| 420 | .i64 => unreachable, |
| 421 | .i32 => return .f32_ceil, // when valtype is f16, we store it in i32. |
| 421 | 422 | .f32 => return .f32_ceil, |
| 422 | 423 | .f64 => return .f64_ceil, |
| 423 | 424 | }, |
| 424 | 425 | .floor => switch (args.valtype1.?) { |
| 425 | | .i32, .i64 => unreachable, |
| 426 | .i64 => unreachable, |
| 427 | .i32 => return .f32_floor, // when valtype is f16, we store it in i32. |
| 426 | 428 | .f32 => return .f32_floor, |
| 427 | 429 | .f64 => return .f64_floor, |
| 428 | 430 | }, |
| 429 | 431 | .trunc => switch (args.valtype1.?) { |
| 430 | | .i32 => switch (args.valtype2.?) { |
| 432 | .i32 => if (args.valtype2) |valty| switch (valty) { |
| 431 | 433 | .i32 => unreachable, |
| 432 | 434 | .i64 => unreachable, |
| 433 | 435 | .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u, |
| 434 | 436 | .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u, |
| 435 | | }, |
| 437 | } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32. |
| 436 | 438 | .i64 => unreachable, |
| 437 | 439 | .f32 => return .f32_trunc, |
| 438 | 440 | .f64 => return .f64_trunc, |
| ... | ... | @@ -788,55 +790,53 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue { |
| 788 | 790 | |
| 789 | 791 | /// Generates a `wasm.Type` from a given function type. |
| 790 | 792 | /// Memory is owned by the caller. |
| 791 | | fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std.Target) !wasm.Type { |
| 792 | | var params = std.ArrayList(wasm.Valtype).init(gpa); |
| 793 | | defer params.deinit(); |
| 793 | fn genFunctype(gpa: Allocator, cc: std.builtin.CallingConvention, params: []const Type, return_type: Type, target: std.Target) !wasm.Type { |
| 794 | var temp_params = std.ArrayList(wasm.Valtype).init(gpa); |
| 795 | defer temp_params.deinit(); |
| 794 | 796 | var returns = std.ArrayList(wasm.Valtype).init(gpa); |
| 795 | 797 | defer returns.deinit(); |
| 796 | 798 | |
| 797 | | if (firstParamSRet(fn_info.cc, fn_info.return_type, target)) { |
| 798 | | try params.append(.i32); // memory address is always a 32-bit handle |
| 799 | | } else if (fn_info.return_type.hasRuntimeBitsIgnoreComptime()) { |
| 800 | | if (fn_info.cc == .C) { |
| 801 | | const res_classes = abi.classifyType(fn_info.return_type, target); |
| 799 | if (firstParamSRet(cc, return_type, target)) { |
| 800 | try temp_params.append(.i32); // memory address is always a 32-bit handle |
| 801 | } else if (return_type.hasRuntimeBitsIgnoreComptime()) { |
| 802 | if (cc == .C) { |
| 803 | const res_classes = abi.classifyType(return_type, target); |
| 802 | 804 | assert(res_classes[0] == .direct and res_classes[1] == .none); |
| 803 | | const scalar_type = abi.scalarType(fn_info.return_type, target); |
| 805 | const scalar_type = abi.scalarType(return_type, target); |
| 804 | 806 | try returns.append(typeToValtype(scalar_type, target)); |
| 805 | 807 | } else { |
| 806 | | try returns.append(typeToValtype(fn_info.return_type, target)); |
| 808 | try returns.append(typeToValtype(return_type, target)); |
| 807 | 809 | } |
| 808 | | } else if (fn_info.return_type.isError()) { |
| 810 | } else if (return_type.isError()) { |
| 809 | 811 | try returns.append(.i32); |
| 810 | 812 | } |
| 811 | 813 | |
| 812 | 814 | // param types |
| 813 | | if (fn_info.param_types.len != 0) { |
| 814 | | for (fn_info.param_types) |param_type| { |
| 815 | | if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; |
| 816 | | |
| 817 | | switch (fn_info.cc) { |
| 818 | | .C => { |
| 819 | | const param_classes = abi.classifyType(param_type, target); |
| 820 | | for (param_classes) |class| { |
| 821 | | if (class == .none) continue; |
| 822 | | if (class == .direct) { |
| 823 | | const scalar_type = abi.scalarType(param_type, target); |
| 824 | | try params.append(typeToValtype(scalar_type, target)); |
| 825 | | } else { |
| 826 | | try params.append(typeToValtype(param_type, target)); |
| 827 | | } |
| 815 | for (params) |param_type| { |
| 816 | if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; |
| 817 | |
| 818 | switch (cc) { |
| 819 | .C => { |
| 820 | const param_classes = abi.classifyType(param_type, target); |
| 821 | for (param_classes) |class| { |
| 822 | if (class == .none) continue; |
| 823 | if (class == .direct) { |
| 824 | const scalar_type = abi.scalarType(param_type, target); |
| 825 | try temp_params.append(typeToValtype(scalar_type, target)); |
| 826 | } else { |
| 827 | try temp_params.append(typeToValtype(param_type, target)); |
| 828 | 828 | } |
| 829 | | }, |
| 830 | | else => if (isByRef(param_type, target)) |
| 831 | | try params.append(.i32) |
| 832 | | else |
| 833 | | try params.append(typeToValtype(param_type, target)), |
| 834 | | } |
| 829 | } |
| 830 | }, |
| 831 | else => if (isByRef(param_type, target)) |
| 832 | try temp_params.append(.i32) |
| 833 | else |
| 834 | try temp_params.append(typeToValtype(param_type, target)), |
| 835 | 835 | } |
| 836 | 836 | } |
| 837 | 837 | |
| 838 | 838 | return wasm.Type{ |
| 839 | | .params = params.toOwnedSlice(), |
| 839 | .params = temp_params.toOwnedSlice(), |
| 840 | 840 | .returns = returns.toOwnedSlice(), |
| 841 | 841 | }; |
| 842 | 842 | } |
| ... | ... | @@ -877,7 +877,8 @@ pub fn generate( |
| 877 | 877 | } |
| 878 | 878 | |
| 879 | 879 | fn genFunc(self: *Self) InnerError!void { |
| 880 | | var func_type = try genFunctype(self.gpa, self.decl.ty.fnInfo(), self.target); |
| 880 | const fn_info = self.decl.ty.fnInfo(); |
| 881 | var func_type = try genFunctype(self.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, self.target); |
| 881 | 882 | defer func_type.deinit(self.gpa); |
| 882 | 883 | self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); |
| 883 | 884 | |
| ... | ... | @@ -1733,7 +1734,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1733 | 1734 | break :blk module.declPtr(func.data.owner_decl); |
| 1734 | 1735 | } else if (func_val.castTag(.extern_fn)) |extern_fn| { |
| 1735 | 1736 | const ext_decl = module.declPtr(extern_fn.data.owner_decl); |
| 1736 | | var func_type = try genFunctype(self.gpa, ext_decl.ty.fnInfo(), self.target); |
| 1737 | const ext_info = ext_decl.ty.fnInfo(); |
| 1738 | var func_type = try genFunctype(self.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, self.target); |
| 1737 | 1739 | defer func_type.deinit(self.gpa); |
| 1738 | 1740 | ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); |
| 1739 | 1741 | try self.bin_file.addOrUpdateImport( |
| ... | ... | @@ -1774,7 +1776,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1774 | 1776 | const operand = try self.resolveInst(pl_op.operand); |
| 1775 | 1777 | try self.emitWValue(operand); |
| 1776 | 1778 | |
| 1777 | | var fn_type = try genFunctype(self.gpa, fn_ty.fnInfo(), self.target); |
| 1779 | var fn_type = try genFunctype(self.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, self.target); |
| 1778 | 1780 | defer fn_type.deinit(self.gpa); |
| 1779 | 1781 | |
| 1780 | 1782 | const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type); |
| ... | ... | @@ -4883,12 +4885,38 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4883 | 4885 | try self.emitWValue(rem_result); |
| 4884 | 4886 | try self.addTag(.select); |
| 4885 | 4887 | } else { |
| 4886 | | const div_result = try self.binOp(lhs, rhs, ty, .div); |
| 4887 | | try self.emitWValue(div_result); |
| 4888 | | switch (ty.floatBits(self.target)) { |
| 4889 | | 32 => try self.addTag(.f32_floor), |
| 4890 | | 64 => try self.addTag(.f64_floor), |
| 4891 | | else => |bit_size| return self.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{bit_size}), |
| 4888 | const float_bits = ty.floatBits(self.target); |
| 4889 | if (float_bits > 64) { |
| 4890 | return self.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{float_bits}); |
| 4891 | } |
| 4892 | const is_f16 = float_bits == 16; |
| 4893 | |
| 4894 | const lhs_operand = if (is_f16) blk: { |
| 4895 | break :blk try self.fpext(lhs, Type.f16, Type.f32); |
| 4896 | } else lhs; |
| 4897 | const rhs_operand = if (is_f16) blk: { |
| 4898 | break :blk try self.fpext(rhs, Type.f16, Type.f32); |
| 4899 | } else rhs; |
| 4900 | |
| 4901 | try self.emitWValue(lhs_operand); |
| 4902 | try self.emitWValue(rhs_operand); |
| 4903 | |
| 4904 | switch (float_bits) { |
| 4905 | 16, 32 => { |
| 4906 | try self.addTag(.f32_div); |
| 4907 | try self.addTag(.f32_floor); |
| 4908 | }, |
| 4909 | 64 => { |
| 4910 | try self.addTag(.f64_div); |
| 4911 | try self.addTag(.f64_floor); |
| 4912 | }, |
| 4913 | else => unreachable, |
| 4914 | } |
| 4915 | |
| 4916 | if (is_f16) { |
| 4917 | // we can re-use temporary local |
| 4918 | try self.addLabel(.local_set, lhs_operand.local); |
| 4919 | return self.fptrunc(lhs_operand, Type.f32, Type.f16); |
| 4892 | 4920 | } |
| 4893 | 4921 | } |
| 4894 | 4922 | |
| ... | ... | @@ -4961,22 +4989,28 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValu |
| 4961 | 4989 | |
| 4962 | 4990 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4963 | 4991 | const ty = self.air.typeOfIndex(inst); |
| 4992 | const float_bits = ty.floatBits(self.target); |
| 4993 | const is_f16 = float_bits == 16; |
| 4964 | 4994 | |
| 4965 | 4995 | if (ty.zigTypeTag() == .Vector) { |
| 4966 | 4996 | return self.fail("TODO: Implement `@ceil` for vectors", .{}); |
| 4967 | 4997 | } |
| 4998 | if (float_bits > 64) { |
| 4999 | return self.fail("TODO: implement `@ceil`, `@trunc`, `@floor` for floats larger than 64bits", .{}); |
| 5000 | } |
| 4968 | 5001 | |
| 4969 | 5002 | const operand = try self.resolveInst(un_op); |
| 4970 | | try self.emitWValue(operand); |
| 4971 | | switch (ty.floatBits(self.target)) { |
| 4972 | | 32, 64 => { |
| 4973 | | const opcode = buildOpcode(.{ |
| 4974 | | .op = op, |
| 4975 | | .valtype1 = typeToValtype(ty, self.target), |
| 4976 | | }); |
| 4977 | | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 4978 | | }, |
| 4979 | | else => |bit_size| return self.fail("TODO: Implement `@ceil` for floats with bitsize {d}", .{bit_size}), |
| 5003 | const op_to_lower = if (is_f16) blk: { |
| 5004 | break :blk try self.fpext(operand, Type.f16, Type.f32); |
| 5005 | } else operand; |
| 5006 | try self.emitWValue(op_to_lower); |
| 5007 | const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target) }); |
| 5008 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 5009 | |
| 5010 | if (is_f16) { |
| 5011 | // re-use temporary to save locals |
| 5012 | try self.addLabel(.local_set, op_to_lower.local); |
| 5013 | return self.fptrunc(op_to_lower, Type.f32, Type.f16); |
| 4980 | 5014 | } |
| 4981 | 5015 | |
| 4982 | 5016 | const result = try self.allocLocal(ty); |
| ... | ... | @@ -5212,19 +5246,8 @@ fn callIntrinsic( |
| 5212 | 5246 | return self.fail("Could not find or create global symbol '{s}'", .{@errorName(err)}); |
| 5213 | 5247 | }; |
| 5214 | 5248 | |
| 5215 | | // TODO: have genFunctype accept individual params so we don't, |
| 5216 | | // need to initialize a fake Fn.Data instance. |
| 5217 | | var pt_tmp = try self.gpa.dupe(Type, param_types); |
| 5218 | | defer self.gpa.free(pt_tmp); |
| 5219 | | var func_type = try genFunctype(self.gpa, .{ |
| 5220 | | .param_types = pt_tmp, |
| 5221 | | .comptime_params = undefined, |
| 5222 | | .return_type = return_type, |
| 5223 | | .alignment = 0, |
| 5224 | | .cc = .C, |
| 5225 | | .is_var_args = false, |
| 5226 | | .is_generic = false, |
| 5227 | | }, self.target); |
| 5249 | // Always pass over C-ABI |
| 5250 | var func_type = try genFunctype(self.gpa, .C, param_types, return_type, self.target); |
| 5228 | 5251 | defer func_type.deinit(self.gpa); |
| 5229 | 5252 | const func_type_index = try self.bin_file.putOrGetFuncType(func_type); |
| 5230 | 5253 | try self.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index); |