Commit 7b52dbbf83

Vexu <git@vexu.eu>
2020-07-20 21:45:27
stage2: implement some casts for numbers
1 parent da217fa
Changed files (4)
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}),
             }
         }
 
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 {
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 });
 }
 
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);