From fd2f034e31bb7b2f8c671158a61473222b5cb332 Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 20 Jul 2020 17:50:37 +0300 Subject: [PATCH 1/7] fix comptime comparisons of different sized floats --- src/ir.cpp | 5 +++-- test/stage1/behavior/floatop.zig | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 11ff7746e7..cf8558761f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -288,6 +288,7 @@ static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* so static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right); static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right); static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field); +static void value_to_bigfloat(BigFloat *out, ZigValue *val); #define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) #define ir_assert_gen(OK, SOURCE_INSTRUCTION) ir_assert_gen_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) @@ -10930,8 +10931,8 @@ static Cmp float_cmp(ZigValue *op1, ZigValue *op2) { } BigFloat op1_big; BigFloat op2_big; - float_init_bigfloat(op1, &op1_big); - float_init_bigfloat(op2, &op2_big); + value_to_bigfloat(&op1_big, op1); + value_to_bigfloat(&op2_big, op2); return bigfloat_cmp(&op1_big, &op2_big); } diff --git a/test/stage1/behavior/floatop.zig b/test/stage1/behavior/floatop.zig index 7dd96476e0..01ad68fe0c 100644 --- a/test/stage1/behavior/floatop.zig +++ b/test/stage1/behavior/floatop.zig @@ -434,6 +434,17 @@ fn testFloatComparisons() void { } } +test "different sized float comparisons" { + testDifferentSizedFloatComparisons(); + comptime testDifferentSizedFloatComparisons(); +} + +fn testDifferentSizedFloatComparisons() void { + var a: f16 = 1; + var b: f64 = 2; + expect(a < b); +} + // TODO This is waiting on library support for the Windows build (not sure why the other's don't need it) //test "@nearbyint" { // comptime testNearbyInt(); From e77ca6af7083da6723dfeb83d3788ebf7012c64e Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 20 Jul 2020 18:11:40 +0300 Subject: [PATCH 2/7] stage2: add float values --- src-self-hosted/value.zig | 161 ++++++++++++++++++++++++++++++++++---- 1 file changed, 144 insertions(+), 17 deletions(-) diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index a3e1daa383..ec69e9bc17 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -80,6 +80,11 @@ pub const Value = extern union { elem_ptr, bytes, repeated, // the value is a value repeated some number of times + float, + float_16, + float_32, + float_64, + float_128, pub const last_no_payload_tag = Tag.bool_false; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -213,6 +218,10 @@ pub const Value = extern union { }; return Value{ .ptr_otherwise = &new_payload.base }; }, + .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16), + .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32), + .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64), + .float_128, .float => return self.copyPayloadShallow(allocator, Payload.Float_128), } } @@ -300,6 +309,10 @@ pub const Value = extern union { try out_stream.writeAll("(repeated) "); val = val.cast(Payload.Repeated).?.val; }, + .float_16 => return out_stream.print("{}", .{val.cast(Payload.Float_16).?.val}), + .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}), + .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}), + .float_128, .float => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}), }; } @@ -380,6 +393,11 @@ pub const Value = extern union { .elem_ptr, .bytes, .repeated, + .float, + .float_16, + .float_32, + .float_64, + .float_128, => unreachable, }; } @@ -435,6 +453,11 @@ pub const Value = extern union { .bytes, .undef, .repeated, + .float, + .float_16, + .float_32, + .float_64, + .float_128, => unreachable, .the_one_possible_value, // An integer with one possible value is always zero. @@ -502,6 +525,11 @@ pub const Value = extern union { .bytes, .undef, .repeated, + .float, + .float_16, + .float_32, + .float_64, + .float_128, => unreachable, .zero, @@ -518,6 +546,25 @@ pub const Value = extern union { } } + pub fn toBool(self: Value) bool { + return switch (self.tag()) { + .bool_true => true, + .bool_false, .zero => false, + else => unreachable, + }; + } + + /// Asserts that the value is a float. + pub fn toF128(self: Value) f128 { + return switch (self.tag()) { + .float_16 => self.cast(Payload.Float_16).?.val, + .float_32 => self.cast(Payload.Float_32).?.val, + .float_64 => self.cast(Payload.Float_64).?.val, + .float_128, .float => self.cast(Payload.Float_128).?.val, + else => unreachable, + }; + } + /// Asserts the value is an integer and not undefined. /// Returns the number of bits the value requires to represent stored in twos complement form. pub fn intBitCountTwosComp(self: Value) usize { @@ -570,6 +617,11 @@ pub const Value = extern union { .bytes, .undef, .repeated, + .float, + .float_16, + .float_32, + .float_64, + .float_128, => unreachable, .the_one_possible_value, // an integer with one possible value is always zero @@ -642,6 +694,11 @@ pub const Value = extern union { .elem_ptr, .bytes, .repeated, + .float, + .float_16, + .float_32, + .float_64, + .float_128, => unreachable, .zero, @@ -762,11 +819,17 @@ pub const Value = extern union { => unreachable, .zero => false, + + .float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0, + .float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0, + .float_64 => @rem(self.cast(Payload.Float_64).?.val, 1) != 0, + // .float_128, .float => @rem(self.cast(Payload.Float_128).?.val, 1) != 0, + .float_128, .float => @panic("TODO lld: error: undefined symbol: fmodl"), }; } pub fn orderAgainstZero(lhs: Value) std.math.Order { - switch (lhs.tag()) { + return switch (lhs.tag()) { .ty, .u8_type, .i8_type, @@ -820,15 +883,20 @@ pub const Value = extern union { .zero, .the_one_possible_value, // an integer with one possible value is always zero .bool_false, - => return .eq, + => .eq, - .bool_true => return .gt, + .bool_true => .gt, - .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0), - .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0), - .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0), - .int_big_negative => return lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0), - } + .int_u64 => std.math.order(lhs.cast(Payload.Int_u64).?.int, 0), + .int_i64 => std.math.order(lhs.cast(Payload.Int_i64).?.int, 0), + .int_big_positive => lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0), + .int_big_negative => lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0), + + .float_16 => std.math.order(lhs.cast(Payload.Float_16).?.val, 0), + .float_32 => std.math.order(lhs.cast(Payload.Float_32).?.val, 0), + .float_64 => std.math.order(lhs.cast(Payload.Float_64).?.val, 0), + .float_128, .float => std.math.order(lhs.cast(Payload.Float_128).?.val, 0), + }; } /// Asserts the value is comparable. @@ -840,7 +908,24 @@ pub const Value = extern union { if (lhs_is_zero) return rhs.orderAgainstZero().invert(); if (rhs_is_zero) return lhs.orderAgainstZero(); - // TODO floats + const lhs_float = lhs.isFloat(); + const rhs_float = rhs.isFloat(); + if (lhs_float and rhs_float) { + if (lhs_tag == rhs_tag) { + return switch (lhs.tag()) { + .float_16 => return std.math.order(lhs.cast(Payload.Float_16).?.val, rhs.cast(Payload.Float_16).?.val), + .float_32 => return std.math.order(lhs.cast(Payload.Float_32).?.val, rhs.cast(Payload.Float_32).?.val), + .float_64 => return std.math.order(lhs.cast(Payload.Float_64).?.val, rhs.cast(Payload.Float_64).?.val), + .float_128, .float => return std.math.order(lhs.cast(Payload.Float_128).?.val, rhs.cast(Payload.Float_128).?.val), + else => unreachable, + }; + } + } + if (lhs_float or rhs_float) { + const lhs_f128 = lhs.toF128(); + const rhs_f128 = rhs.toF128(); + return std.math.order(lhs_f128, rhs_f128); + } var lhs_bigint_space: BigIntSpace = undefined; var rhs_bigint_space: BigIntSpace = undefined; @@ -864,14 +949,6 @@ pub const Value = extern union { return compare(a, .eq, b); } - pub fn toBool(self: Value) bool { - return switch (self.tag()) { - .bool_true => true, - .bool_false, .zero => false, - else => unreachable, - }; - } - /// Asserts the value is a pointer and dereferences it. /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis. pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value { @@ -928,6 +1005,11 @@ pub const Value = extern union { .bytes, .undef, .repeated, + .float, + .float_16, + .float_32, + .float_64, + .float_128, => unreachable, .the_one_possible_value => Value.initTag(.the_one_possible_value), @@ -999,6 +1081,11 @@ pub const Value = extern union { .elem_ptr, .ref_val, .decl_ref, + .float, + .float_16, + .float_32, + .float_64, + .float_128, => unreachable, .bytes => { @@ -1085,6 +1172,11 @@ pub const Value = extern union { .elem_ptr, .bytes, .repeated, + .float, + .float_16, + .float_32, + .float_64, + .float_128, => false, .undef => unreachable, @@ -1092,6 +1184,21 @@ pub const Value = extern union { }; } + /// Valid for all types. Asserts the value is not undefined. + pub fn isFloat(self: Value) bool { + return switch (self.tag()) { + .undef => unreachable, + + .float, + .float_16, + .float_32, + .float_64, + .float_128, + => true, + else => false, + }; + } + /// This type is not copyable since it may contain pointers to its inner data. pub const Payload = struct { tag: Tag, @@ -1168,6 +1275,26 @@ pub const Value = extern union { /// is stored externally. val: Value, }; + + pub const Float_16 = struct { + base: Payload = .{ .tag = .float_16 }, + val: f16, + }; + + pub const Float_32 = struct { + base: Payload = .{ .tag = .float_32 }, + val: f32, + }; + + pub const Float_64 = struct { + base: Payload = .{ .tag = .float_64 }, + val: f64, + }; + + pub const Float_128 = struct { + base: Payload = .{ .tag = .float_128 }, + val: f128, + }; }; /// Big enough to fit any non-BigInt value From da217fadeb888ba1286e36ab42bdbe9918426a0a Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 20 Jul 2020 18:34:06 +0300 Subject: [PATCH 3/7] stage2: astgen for floats and other primitive literals --- src-self-hosted/astgen.zig | 48 ++++++++++++++++++++++++++++++++++++++ src-self-hosted/value.zig | 9 ++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 2365cb9b14..6f0bcf6dc5 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -38,6 +38,8 @@ pub fn expr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { .Period => return field(mod, scope, node.castTag(.Period).?), .Deref => return deref(mod, scope, node.castTag(.Deref).?), .BoolNot => return boolNot(mod, scope, node.castTag(.BoolNot).?), + .FloatLiteral => return floatLiteral(mod, scope, node.castTag(.FloatLiteral).?), + .UndefinedLiteral, .BoolLiteral, .NullLiteral => return primitiveLiteral(mod, scope, node), else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}), } } @@ -405,6 +407,52 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral } } +fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.FloatLiteral) InnerError!*zir.Inst { + const arena = scope.arena(); + const tree = scope.tree(); + const bytes = tree.tokenSlice(float_lit.token); + if (bytes.len > 2 and bytes[1] == 'x') { + return mod.failTok(scope, float_lit.token, "TODO hex floats", .{}); + } + + const val = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) { + error.InvalidCharacter => unreachable, // validated by tokenizer + }; + const float_payload = try arena.create(Value.Payload.Float_128); + float_payload.* = .{ .val = val }; + const src = tree.token_locs[float_lit.token].start; + return mod.addZIRInstConst(scope, src, .{ + .ty = Type.initTag(.comptime_float), + .val = Value.initPayload(&float_payload.base), + }); +} + +fn primitiveLiteral(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { + const arena = scope.arena(); + const tree = scope.tree(); + const src = tree.token_locs[node.firstToken()].start; + + if (node.cast(ast.Node.BoolLiteral)) |bool_node| { + return mod.addZIRInstConst(scope, src, .{ + .ty = Type.initTag(.bool), + .val = if (tree.token_ids[bool_node.token] == .Keyword_true) + Value.initTag(.bool_true) + else + Value.initTag(.bool_false), + }); + } else if (node.tag == .UndefinedLiteral) { + return mod.addZIRInstConst(scope, src, .{ + .ty = Type.initTag(.@"undefined"), + .val = Value.initTag(.undef), + }); + } else if (node.tag == .NullLiteral) { + return mod.addZIRInstConst(scope, src, .{ + .ty = Type.initTag(.@"null"), + .val = Value.initTag(.null_value), + }); + } else unreachable; +} + fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst { if (asm_node.outputs.len != 0) { return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{}); diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index ec69e9bc17..e54836ba11 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -554,13 +554,20 @@ pub const Value = extern union { }; } - /// Asserts that the value is a float. + /// Asserts that the value is a float or an integer. pub fn toF128(self: Value) f128 { return switch (self.tag()) { .float_16 => self.cast(Payload.Float_16).?.val, .float_32 => self.cast(Payload.Float_32).?.val, .float_64 => self.cast(Payload.Float_64).?.val, .float_128, .float => self.cast(Payload.Float_128).?.val, + + .zero, .the_one_possible_value => 0, + .int_u64 => @intToFloat(f128, self.cast(Payload.Int_u64).?.int), + // .int_i64 => @intToFloat(f128, self.cast(Payload.Int_i64).?.int), + .int_i64 => @panic("TODO lld: error: undefined symbol: __floatditf"), + + .int_big_positive, .int_big_negative => @panic("big int to f128"), else => unreachable, }; } From 7b52dbbf83ec89b9337e2be7328480e83489dc0d Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 20 Jul 2020 22:45:27 +0300 Subject: [PATCH 4/7] stage2: implement some casts for numbers --- src-self-hosted/Module.zig | 62 ++++++++++++++++++++++++++++--------- src-self-hosted/codegen.zig | 10 ++++++ src-self-hosted/ir.zig | 11 +++++++ src-self-hosted/zir.zig | 6 ++++ 4 files changed, 74 insertions(+), 15 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 19b683b177..0376d5a11d 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -3358,6 +3358,14 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { return self.bitcast(scope, dest_type, inst); } + // undefined to anything + if (inst.value()) |val| { + if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) { + return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); + } + } + assert(inst.ty.zigTypeTag() != .Undefined); + // *[N]T to []T if (inst.ty.isSinglePointer() and dest_type.isSlice() and (!inst.ty.pointerIsConst() or dest_type.pointerIsConst())) @@ -3371,34 +3379,58 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { } } - // comptime_int to fixed-width integer - if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) { - // The representation is already correct; we only need to make sure it fits in the destination type. - const val = inst.value().?; // comptime_int always has comptime known value - if (!val.intFitsInType(dest_type, self.target())) { - return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); + // comptime known number to other number + if (inst.value()) |val| { + const src_zig_tag = inst.ty.zigTypeTag(); + const dst_zig_tag = dest_type.zigTypeTag(); + + if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) { + if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { + if (val.floatHasFraction()) { + return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty }); + } + return self.fail(scope, inst.src, "TODO float to int", .{}); + } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { + if (!val.intFitsInType(dest_type, self.target())) { + return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); + } + return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); + } + } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { + if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { + return self.fail(scope, inst.src, "TODO float cast", .{}); + } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { + return self.fail(scope, inst.src, "TODO int to float", .{}); + } } - return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); } // integer widening if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { + assert(inst.value() == null); // handled above + const src_info = inst.ty.intInfo(self.target()); const dst_info = dest_type.intInfo(self.target()); if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) { - if (inst.value()) |val| { - return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); - } else { - return self.fail(scope, inst.src, "TODO implement runtime integer widening ({} to {})", .{ - inst.ty, - dest_type, - }); - } + const b = try self.requireRuntimeBlock(scope, inst.src); + return self.addNewInstArgs(b, inst.src, dest_type, Inst.WidenOrShorten, .{ .operand = inst }); } else { return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type }); } } + // float widening + if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) { + assert(inst.value() == null); // handled above + + const src_bits = inst.ty.floatBits(self.target()); + const dst_bits = dest_type.floatBits(self.target()); + if (dst_bits >= src_bits) { + const b = try self.requireRuntimeBlock(scope, inst.src); + return self.addNewInstArgs(b, inst.src, dest_type, Inst.WidenOrShorten, .{ .operand = inst }); + } + } + return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type }); } diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index d64c1824cf..7db3bcb4aa 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -459,6 +459,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .sub => return self.genSub(inst.castTag(.sub).?), .unreach => return MCValue{ .unreach = {} }, .not => return self.genNot(inst.castTag(.not).?), + .widenorshorten => return self.genWidenOrShorten(isnt.castTag(.widenorshorten).?), + } + } + + fn genWidenOrShorten(self: *Self, inst: *ir.Inst.WidenOrShorten) !MCValue { + // No side effects, so if it's unreferenced, do nothing. + if (inst.base.isUnused()) + return MCValue.dead; + switch (arch) { + else => return self.fail(inst.base.src, "TODO implement widen or shorten for {}", .{self.target.cpu.arch}), } } diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 53a73dbf6c..4d82920e52 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -71,6 +71,7 @@ pub const Inst = struct { sub, unreach, not, + widenorshorten, /// There is one-to-one correspondence between tag and type for now, /// but this will not always be the case. For example, binary operations @@ -108,6 +109,7 @@ pub const Inst = struct { .call => Call, .condbr => CondBr, .constant => Constant, + .widenorshorten => WidenOrShorten, }; } @@ -374,6 +376,15 @@ pub const Inst = struct { return null; } }; + + pub const WidenOrShorten = struct { + pub const base_tag = Tag.widenorshorten; + + base: Inst, + args: struct { + operand: *Inst, + }, + }; }; pub const Body = struct { diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 1afa37ec28..3debb1efe4 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -558,6 +558,7 @@ pub const Inst = struct { pub const IntCast = struct { pub const base_tag = Tag.intcast; + pub const builtin_name = "@intCast"; base: Inst, positionals: struct { @@ -569,6 +570,7 @@ pub const Inst = struct { pub const BitCast = struct { pub const base_tag = Tag.bitcast; + pub const builtin_name = "@bitCast"; base: Inst, positionals: struct { @@ -1820,6 +1822,10 @@ const EmitZIR = struct { }; break :blk &new_inst.base; }, + .widenorshorten => blk: { + const old_inst = inst.cast(ir.Inst.WidenOrShorten).?; + break :blk try self.resolveInst(new_body, old_inst.args.operand); + }, }; try instructions.append(new_inst); try inst_table.put(inst, new_inst); From 7e7d1df4daedecf32d97ad21c85e23bcaac29e7a Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 21 Jul 2020 17:14:40 +0300 Subject: [PATCH 5/7] stage2: add floatCast to zir and ir --- src-self-hosted/Module.zig | 70 +++++++++++++++++++++++++++++-------- src-self-hosted/codegen.zig | 16 +++++++-- src-self-hosted/ir.zig | 15 +++----- src-self-hosted/value.zig | 24 ++++--------- src-self-hosted/zir.zig | 62 +++++++++++++++++++++----------- 5 files changed, 120 insertions(+), 67 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 0376d5a11d..1ebed4e09a 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -2352,6 +2352,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In .fntype => return self.analyzeInstFnType(scope, old_inst.castTag(.fntype).?), .intcast => return self.analyzeInstIntCast(scope, old_inst.castTag(.intcast).?), .bitcast => return self.analyzeInstBitCast(scope, old_inst.castTag(.bitcast).?), + .floatcast => return self.analyzeInstFloatCast(scope, old_inst.castTag(.floatcast).?), .elemptr => return self.analyzeInstElemPtr(scope, old_inst.castTag(.elemptr).?), .add => return self.analyzeInstAdd(scope, old_inst.castTag(.add).?), .sub => return self.analyzeInstSub(scope, old_inst.castTag(.sub).?), @@ -2796,16 +2797,16 @@ fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPt } } -fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *zir.Inst.IntCast) InnerError!*Inst { - const dest_type = try self.resolveType(scope, intcast.positionals.dest_type); - const new_inst = try self.resolveInst(scope, intcast.positionals.value); +fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.IntCast) InnerError!*Inst { + const dest_type = try self.resolveType(scope, inst.positionals.dest_type); + const operand = try self.resolveInst(scope, inst.positionals.operand); const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { .ComptimeInt => true, .Int => false, else => return self.fail( scope, - intcast.positionals.dest_type.src, + inst.positionals.dest_type.src, "expected integer type, found '{}'", .{ dest_type, @@ -2813,21 +2814,23 @@ fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *zir.Inst.IntCast) ), }; - switch (new_inst.ty.zigTypeTag()) { + switch (operand.ty.zigTypeTag()) { .ComptimeInt, .Int => {}, else => return self.fail( scope, - intcast.positionals.value.src, + inst.positionals.operand.src, "expected integer type, found '{}'", - .{new_inst.ty}, + .{operand.ty}, ), } - if (dest_is_comptime_int or new_inst.value() != null) { - return self.coerce(scope, dest_type, new_inst); + if (operand.value() != null) { + return self.coerce(scope, dest_type, operand); + } else if (dest_is_comptime_int) { + return self.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{}); } - return self.fail(scope, intcast.base.src, "TODO implement analyze widen or shorten int", .{}); + return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{}); } fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BitCast) InnerError!*Inst { @@ -2836,6 +2839,42 @@ fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BitCast) Inn return self.bitcast(scope, dest_type, operand); } +fn analyzeInstFloatCast(self: *Module, scope: *Scope, inst: *zir.Inst.FloatCast) InnerError!*Inst { + const dest_type = try self.resolveType(scope, inst.positionals.dest_type); + const operand = try self.resolveInst(scope, inst.positionals.operand); + + const dest_is_comptime_float = switch (dest_type.zigTypeTag()) { + .ComptimeFloat => true, + .Float => false, + else => return self.fail( + scope, + inst.positionals.dest_type.src, + "expected float type, found '{}'", + .{ + dest_type, + }, + ), + }; + + switch (operand.ty.zigTypeTag()) { + .ComptimeFloat, .Float, .ComptimeInt => {}, + else => return self.fail( + scope, + inst.positionals.operand.src, + "expected float type, found '{}'", + .{operand.ty}, + ), + } + + if (operand.value() != null) { + return self.coerce(scope, dest_type, operand); + } else if (dest_is_comptime_float) { + return self.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{}); + } + + return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{}); +} + fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst { const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr); const uncasted_index = try self.resolveInst(scope, inst.positionals.index); @@ -3411,11 +3450,12 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { const src_info = inst.ty.intInfo(self.target()); const dst_info = dest_type.intInfo(self.target()); - if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) { + if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or + // small enough unsigned ints can get casted to large enough signed ints + (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits)) + { const b = try self.requireRuntimeBlock(scope, inst.src); - return self.addNewInstArgs(b, inst.src, dest_type, Inst.WidenOrShorten, .{ .operand = inst }); - } else { - return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type }); + return self.addUnOp(b, inst.src, dest_type, .intcast, inst); } } @@ -3427,7 +3467,7 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { const dst_bits = dest_type.floatBits(self.target()); if (dst_bits >= src_bits) { const b = try self.requireRuntimeBlock(scope, inst.src); - return self.addNewInstArgs(b, inst.src, dest_type, Inst.WidenOrShorten, .{ .operand = inst }); + return self.addUnOp(b, inst.src, dest_type, .floatcast, inst); } } diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 7db3bcb4aa..7e71cc55db 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -459,16 +459,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .sub => return self.genSub(inst.castTag(.sub).?), .unreach => return MCValue{ .unreach = {} }, .not => return self.genNot(inst.castTag(.not).?), - .widenorshorten => return self.genWidenOrShorten(isnt.castTag(.widenorshorten).?), + .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?), + .intcast => return self.genIntCast(inst.castTag(.intcast).?), } } - fn genWidenOrShorten(self: *Self, inst: *ir.Inst.WidenOrShorten) !MCValue { + fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue { // No side effects, so if it's unreferenced, do nothing. if (inst.base.isUnused()) return MCValue.dead; switch (arch) { - else => return self.fail(inst.base.src, "TODO implement widen or shorten for {}", .{self.target.cpu.arch}), + else => return self.fail(inst.base.src, "TODO implement floatCast for {}", .{self.target.cpu.arch}), + } + } + + fn genIntCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue { + // No side effects, so if it's unreferenced, do nothing. + if (inst.base.isUnused()) + return MCValue.dead; + switch (arch) { + else => return self.fail(inst.base.src, "TODO implement intCast for {}", .{self.target.cpu.arch}), } } diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 4d82920e52..f501627691 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -71,7 +71,8 @@ pub const Inst = struct { sub, unreach, not, - widenorshorten, + floatcast, + intcast, /// There is one-to-one correspondence between tag and type for now, /// but this will not always be the case. For example, binary operations @@ -90,6 +91,8 @@ pub const Inst = struct { .isnonnull, .isnull, .ptrtoint, + .floatcast, + .intcast, => UnOp, .add, @@ -109,7 +112,6 @@ pub const Inst = struct { .call => Call, .condbr => CondBr, .constant => Constant, - .widenorshorten => WidenOrShorten, }; } @@ -376,15 +378,6 @@ pub const Inst = struct { return null; } }; - - pub const WidenOrShorten = struct { - pub const base_tag = Tag.widenorshorten; - - base: Inst, - args: struct { - operand: *Inst, - }, - }; }; pub const Body = struct { diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index e54836ba11..8ca43c398b 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -80,7 +80,6 @@ pub const Value = extern union { elem_ptr, bytes, repeated, // the value is a value repeated some number of times - float, float_16, float_32, float_64, @@ -221,7 +220,7 @@ pub const Value = extern union { .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16), .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32), .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64), - .float_128, .float => return self.copyPayloadShallow(allocator, Payload.Float_128), + .float_128 => return self.copyPayloadShallow(allocator, Payload.Float_128), } } @@ -312,7 +311,7 @@ pub const Value = extern union { .float_16 => return out_stream.print("{}", .{val.cast(Payload.Float_16).?.val}), .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}), .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}), - .float_128, .float => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}), + .float_128 => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}), }; } @@ -393,7 +392,6 @@ pub const Value = extern union { .elem_ptr, .bytes, .repeated, - .float, .float_16, .float_32, .float_64, @@ -453,7 +451,6 @@ pub const Value = extern union { .bytes, .undef, .repeated, - .float, .float_16, .float_32, .float_64, @@ -525,7 +522,6 @@ pub const Value = extern union { .bytes, .undef, .repeated, - .float, .float_16, .float_32, .float_64, @@ -560,7 +556,7 @@ pub const Value = extern union { .float_16 => self.cast(Payload.Float_16).?.val, .float_32 => self.cast(Payload.Float_32).?.val, .float_64 => self.cast(Payload.Float_64).?.val, - .float_128, .float => self.cast(Payload.Float_128).?.val, + .float_128 => self.cast(Payload.Float_128).?.val, .zero, .the_one_possible_value => 0, .int_u64 => @intToFloat(f128, self.cast(Payload.Int_u64).?.int), @@ -624,7 +620,6 @@ pub const Value = extern union { .bytes, .undef, .repeated, - .float, .float_16, .float_32, .float_64, @@ -701,7 +696,6 @@ pub const Value = extern union { .elem_ptr, .bytes, .repeated, - .float, .float_16, .float_32, .float_64, @@ -830,8 +824,8 @@ pub const Value = extern union { .float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0, .float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0, .float_64 => @rem(self.cast(Payload.Float_64).?.val, 1) != 0, - // .float_128, .float => @rem(self.cast(Payload.Float_128).?.val, 1) != 0, - .float_128, .float => @panic("TODO lld: error: undefined symbol: fmodl"), + // .float_128 => @rem(self.cast(Payload.Float_128).?.val, 1) != 0, + .float_128 => @panic("TODO lld: error: undefined symbol: fmodl"), }; } @@ -902,7 +896,7 @@ pub const Value = extern union { .float_16 => std.math.order(lhs.cast(Payload.Float_16).?.val, 0), .float_32 => std.math.order(lhs.cast(Payload.Float_32).?.val, 0), .float_64 => std.math.order(lhs.cast(Payload.Float_64).?.val, 0), - .float_128, .float => std.math.order(lhs.cast(Payload.Float_128).?.val, 0), + .float_128 => std.math.order(lhs.cast(Payload.Float_128).?.val, 0), }; } @@ -923,7 +917,7 @@ pub const Value = extern union { .float_16 => return std.math.order(lhs.cast(Payload.Float_16).?.val, rhs.cast(Payload.Float_16).?.val), .float_32 => return std.math.order(lhs.cast(Payload.Float_32).?.val, rhs.cast(Payload.Float_32).?.val), .float_64 => return std.math.order(lhs.cast(Payload.Float_64).?.val, rhs.cast(Payload.Float_64).?.val), - .float_128, .float => return std.math.order(lhs.cast(Payload.Float_128).?.val, rhs.cast(Payload.Float_128).?.val), + .float_128 => return std.math.order(lhs.cast(Payload.Float_128).?.val, rhs.cast(Payload.Float_128).?.val), else => unreachable, }; } @@ -1012,7 +1006,6 @@ pub const Value = extern union { .bytes, .undef, .repeated, - .float, .float_16, .float_32, .float_64, @@ -1088,7 +1081,6 @@ pub const Value = extern union { .elem_ptr, .ref_val, .decl_ref, - .float, .float_16, .float_32, .float_64, @@ -1179,7 +1171,6 @@ pub const Value = extern union { .elem_ptr, .bytes, .repeated, - .float, .float_16, .float_32, .float_64, @@ -1196,7 +1187,6 @@ pub const Value = extern union { return switch (self.tag()) { .undef => unreachable, - .float, .float_16, .float_32, .float_64, diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 3debb1efe4..d2df0afaf5 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -76,6 +76,7 @@ pub const Inst = struct { primitive, intcast, bitcast, + floatcast, elemptr, add, sub, @@ -137,6 +138,7 @@ pub const Inst = struct { .fntype => FnType, .intcast => IntCast, .bitcast => BitCast, + .floatcast => FloatCast, .elemptr => ElemPtr, .condbr => CondBr, }; @@ -169,6 +171,7 @@ pub const Inst = struct { .primitive, .intcast, .bitcast, + .floatcast, .elemptr, .add, .sub, @@ -556,6 +559,18 @@ pub const Inst = struct { }; }; + pub const FloatCast = struct { + pub const base_tag = Tag.floatcast; + pub const builtin_name = "@floatCast"; + base: Inst, + + positionals: struct { + dest_type: *Inst, + operand: *Inst, + }, + kw_args: struct {}, + }; + pub const IntCast = struct { pub const base_tag = Tag.intcast; pub const builtin_name = "@intCast"; @@ -563,7 +578,7 @@ pub const Inst = struct { positionals: struct { dest_type: *Inst, - value: *Inst, + operand: *Inst, }, kw_args: struct {}, }; @@ -1620,6 +1635,28 @@ const EmitZIR = struct { return &new_inst.base; } + fn emitCast( + self: *EmitZIR, + src: usize, + new_body: ZirBody, + old_inst: *ir.Inst.UnOp, + comptime I: type, + ) Allocator.Error!*Inst { + const new_inst = try self.arena.allocator.create(I); + new_inst.* = .{ + .base = .{ + .src = src, + .tag = I.base_tag, + }, + .positionals = .{ + .dest_type = (try self.emitType(src, old_inst.base.ty)).inst, + .operand = try self.resolveInst(new_body, old_inst.operand), + }, + .kw_args = .{}, + }; + return &new_inst.base; + } + fn emitBody( self: *EmitZIR, body: ir.Body, @@ -1654,22 +1691,9 @@ const EmitZIR = struct { .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt), .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq), - .bitcast => blk: { - const old_inst = inst.castTag(.bitcast).?; - const new_inst = try self.arena.allocator.create(Inst.BitCast); - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.BitCast.base_tag, - }, - .positionals = .{ - .dest_type = (try self.emitType(inst.src, inst.ty)).inst, - .operand = try self.resolveInst(new_body, old_inst.operand), - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, + .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, Inst.BitCast), + .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, Inst.IntCast), + .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, Inst.FloatCast), .block => blk: { const old_inst = inst.castTag(.block).?; @@ -1822,10 +1846,6 @@ const EmitZIR = struct { }; break :blk &new_inst.base; }, - .widenorshorten => blk: { - const old_inst = inst.cast(ir.Inst.WidenOrShorten).?; - break :blk try self.resolveInst(new_body, old_inst.args.operand); - }, }; try instructions.append(new_inst); try inst_table.put(inst, new_inst); From c29c79b17aa4b8ce5fe35ae1fd4f06201ab059ba Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 21 Jul 2020 21:18:51 +0300 Subject: [PATCH 6/7] stage2: remove some dead code, fix build on aarch64 --- build.zig | 2 ++ src-self-hosted/astgen.zig | 33 ++++++++------------------------- src-self-hosted/value.zig | 27 ++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/build.zig b/build.zig index e4ed04ea16..a5e430fb01 100644 --- a/build.zig +++ b/build.zig @@ -13,6 +13,7 @@ const InstallDirectoryOptions = std.build.InstallDirectoryOptions; pub fn build(b: *Builder) !void { b.setPreferredReleaseMode(.ReleaseFast); const mode = b.standardReleaseOptions(); + const target = b.standardTargetOptions(.{}); var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); @@ -54,6 +55,7 @@ pub fn build(b: *Builder) !void { if (!only_install_lib_files) { var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); exe.setBuildMode(mode); + exe.setTarget(target); test_step.dependOn(&exe.step); b.default_step.dependOn(&exe.step); diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 6f0bcf6dc5..2a5153448c 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -325,7 +325,14 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerErr 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type), 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type), 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type), - else => return mod.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}), + else => { + const int_type_payload = try scope.arena().create(Value.Payload.IntType); + int_type_payload.* = .{ .signed = is_signed, .bits = bit_count }; + return mod.addZIRInstConst(scope, src, .{ + .ty = Type.initTag(.comptime_int), + .val = Value.initPayload(&int_type_payload.base), + }); + }, }; return mod.addZIRInstConst(scope, src, .{ .ty = Type.initTag(.type), @@ -582,30 +589,6 @@ fn getSimplePrimitiveValue(name: []const u8) ?TypedValue { .val = Value.initTag(tag), }; } - if (mem.eql(u8, name, "null")) { - return TypedValue{ - .ty = Type.initTag(.@"null"), - .val = Value.initTag(.null_value), - }; - } - if (mem.eql(u8, name, "undefined")) { - return TypedValue{ - .ty = Type.initTag(.@"undefined"), - .val = Value.initTag(.undef), - }; - } - if (mem.eql(u8, name, "true")) { - return TypedValue{ - .ty = Type.initTag(.bool), - .val = Value.initTag(.bool_true), - }; - } - if (mem.eql(u8, name, "false")) { - return TypedValue{ - .ty = Type.initTag(.bool), - .val = Value.initTag(.bool_false), - }; - } return null; } diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 8ca43c398b..1ad6e6558c 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -70,6 +70,7 @@ pub const Value = extern union { // After this, the tag requires a payload. ty, + int_type, int_u64, int_i64, int_big_positive, @@ -178,6 +179,7 @@ pub const Value = extern union { }; return Value{ .ptr_otherwise = &new_payload.base }; }, + .int_type => return self.copyPayloadShallow(allocator, Payload.IntType), .int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64), .int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64), .int_big_positive => { @@ -287,6 +289,13 @@ pub const Value = extern union { .bool_true => return out_stream.writeAll("true"), .bool_false => return out_stream.writeAll("false"), .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream), + .int_type => { + const int_type = val.cast(Payload.IntType).?; + return out_stream.print("{}{}", .{ + if (int_type.signed) "s" else "u", + int_type.bits, + }); + }, .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream), .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream), .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}), @@ -335,6 +344,7 @@ pub const Value = extern union { pub fn toType(self: Value) Type { return switch (self.tag()) { .ty => self.cast(Payload.Ty).?.ty, + .int_type => @panic("TODO int type to type"), .u8_type => Type.initTag(.u8), .i8_type => Type.initTag(.i8), @@ -404,6 +414,7 @@ pub const Value = extern union { pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst { switch (self.tag()) { .ty, + .int_type, .u8_type, .i8_type, .u16_type, @@ -475,6 +486,7 @@ pub const Value = extern union { pub fn toUnsignedInt(self: Value) u64 { switch (self.tag()) { .ty, + .int_type, .u8_type, .i8_type, .u16_type, @@ -553,7 +565,7 @@ pub const Value = extern union { /// Asserts that the value is a float or an integer. pub fn toF128(self: Value) f128 { return switch (self.tag()) { - .float_16 => self.cast(Payload.Float_16).?.val, + .float_16 => @panic("TODO soft float"), .float_32 => self.cast(Payload.Float_32).?.val, .float_64 => self.cast(Payload.Float_64).?.val, .float_128 => self.cast(Payload.Float_128).?.val, @@ -573,6 +585,7 @@ pub const Value = extern union { pub fn intBitCountTwosComp(self: Value) usize { switch (self.tag()) { .ty, + .int_type, .u8_type, .i8_type, .u16_type, @@ -650,6 +663,7 @@ pub const Value = extern union { pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { switch (self.tag()) { .ty, + .int_type, .u8_type, .i8_type, .u16_type, @@ -763,6 +777,7 @@ pub const Value = extern union { pub fn floatHasFraction(self: Value) bool { return switch (self.tag()) { .ty, + .int_type, .u8_type, .i8_type, .u16_type, @@ -832,6 +847,7 @@ pub const Value = extern union { pub fn orderAgainstZero(lhs: Value) std.math.Order { return switch (lhs.tag()) { .ty, + .int_type, .u8_type, .i8_type, .u16_type, @@ -955,6 +971,7 @@ pub const Value = extern union { pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value { return switch (self.tag()) { .ty, + .int_type, .u8_type, .i8_type, .u16_type, @@ -1028,6 +1045,7 @@ pub const Value = extern union { pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value { switch (self.tag()) { .ty, + .int_type, .u8_type, .i8_type, .u16_type, @@ -1118,6 +1136,7 @@ pub const Value = extern union { pub fn isNull(self: Value) bool { return switch (self.tag()) { .ty, + .int_type, .u8_type, .i8_type, .u16_type, @@ -1266,6 +1285,12 @@ pub const Value = extern union { ty: Type, }; + pub const IntType = struct { + base: Payload = Payload{ .tag = .int_type }, + bits: u16, + signed: bool, + }; + pub const Repeated = struct { base: Payload = Payload{ .tag = .ty }, /// This value is repeated some number of times. The amount of times to repeat From dd8929738876544d9b83c55df6e2ee3a491298ca Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 21 Jul 2020 21:43:40 +0300 Subject: [PATCH 7/7] stage2: actually implement float casting --- src-self-hosted/Module.zig | 11 ++++++- src-self-hosted/value.zig | 60 +++++++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 1ebed4e09a..13c5a065e2 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -3437,7 +3437,16 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { } } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { - return self.fail(scope, inst.src, "TODO float cast", .{}); + const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) { + error.Overflow => return self.fail( + scope, + inst.src, + "cast of value {} to type '{}' loses information", + .{ val, dest_type }, + ), + error.OutOfMemory => return error.OutOfMemory, + }; + return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res }); } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { return self.fail(scope, inst.src, "TODO int to float", .{}); } diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 1ad6e6558c..881602d76a 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -563,15 +563,15 @@ pub const Value = extern union { } /// Asserts that the value is a float or an integer. - pub fn toF128(self: Value) f128 { + pub fn toFloat(self: Value, comptime T: type) T { return switch (self.tag()) { .float_16 => @panic("TODO soft float"), - .float_32 => self.cast(Payload.Float_32).?.val, - .float_64 => self.cast(Payload.Float_64).?.val, - .float_128 => self.cast(Payload.Float_128).?.val, + .float_32 => @floatCast(T, self.cast(Payload.Float_32).?.val), + .float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val), + .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val), .zero, .the_one_possible_value => 0, - .int_u64 => @intToFloat(f128, self.cast(Payload.Int_u64).?.int), + .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int), // .int_i64 => @intToFloat(f128, self.cast(Payload.Int_i64).?.int), .int_i64 => @panic("TODO lld: error: undefined symbol: __floatditf"), @@ -773,6 +773,50 @@ pub const Value = extern union { } } + /// Converts an integer or a float to a float. + /// Returns `error.Overflow` if the value does not fit in the new type. + pub fn floatCast(self: Value, allocator: *Allocator, ty: Type, target: Target) !Value { + const dest_bit_count = switch (ty.tag()) { + .comptime_float => 128, + else => ty.floatBits(target), + }; + switch (dest_bit_count) { + 16, 32, 64, 128 => {}, + else => std.debug.panic("TODO float cast bit count {}\n", .{dest_bit_count}), + } + if (ty.isInt()) { + @panic("TODO int to float"); + } + + switch (dest_bit_count) { + 16 => { + @panic("TODO soft float"); + // var res_payload = Value.Payload.Float_16{.val = self.toFloat(f16)}; + // if (!self.eql(Value.initPayload(&res_payload.base))) + // return error.Overflow; + // return Value.initPayload(&res_payload.base).copy(allocator); + }, + 32 => { + var res_payload = Value.Payload.Float_32{.val = self.toFloat(f32)}; + if (!self.eql(Value.initPayload(&res_payload.base))) + return error.Overflow; + return Value.initPayload(&res_payload.base).copy(allocator); + }, + 64 => { + var res_payload = Value.Payload.Float_64{.val = self.toFloat(f64)}; + if (!self.eql(Value.initPayload(&res_payload.base))) + return error.Overflow; + return Value.initPayload(&res_payload.base).copy(allocator); + }, + 128 => { + const float_payload = try allocator.create(Value.Payload.Float_128); + float_payload.* = .{ .val = self.toFloat(f128) }; + return Value.initPayload(&float_payload.base); + }, + else => unreachable, + } + } + /// Asserts the value is a float pub fn floatHasFraction(self: Value) bool { return switch (self.tag()) { @@ -919,7 +963,7 @@ pub const Value = extern union { /// Asserts the value is comparable. pub fn order(lhs: Value, rhs: Value) std.math.Order { const lhs_tag = lhs.tag(); - const rhs_tag = lhs.tag(); + const rhs_tag = rhs.tag(); const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value; const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value; if (lhs_is_zero) return rhs.orderAgainstZero().invert(); @@ -939,8 +983,8 @@ pub const Value = extern union { } } if (lhs_float or rhs_float) { - const lhs_f128 = lhs.toF128(); - const rhs_f128 = rhs.toF128(); + const lhs_f128 = lhs.toFloat(f128); + const rhs_f128 = rhs.toFloat(f128); return std.math.order(lhs_f128, rhs_f128); }