| author | |
| committer | |
| log | 25a01a16e0eb729857f8e9a0a87bd0b8e9da301b |
| tree | 4f6607c2f58751a0e6c8cede44f8a6b784501714 |
| parent | 68e0632aa531b33669bfdda374675aa26e91b40f |
| parent | dd8929738876544d9b83c55df6e2ee3a491298ca |
| signature |
Stage2: floats9 files changed, 459 insertions(+), 90 deletions(-)
build.zig+2| ... | @@ -13,6 +13,7 @@ const InstallDirectoryOptions = std.build.InstallDirectoryOptions; | ... | @@ -13,6 +13,7 @@ const InstallDirectoryOptions = std.build.InstallDirectoryOptions; |
| 13 | pub fn build(b: *Builder) !void { | 13 | pub fn build(b: *Builder) !void { |
| 14 | b.setPreferredReleaseMode(.ReleaseFast); | 14 | b.setPreferredReleaseMode(.ReleaseFast); |
| 15 | const mode = b.standardReleaseOptions(); | 15 | const mode = b.standardReleaseOptions(); |
| 16 | const target = b.standardTargetOptions(.{}); | ||
| 16 | 17 | ||
| 17 | var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); | 18 | var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); |
| 18 | 19 | ||
| ... | @@ -54,6 +55,7 @@ pub fn build(b: *Builder) !void { | ... | @@ -54,6 +55,7 @@ pub fn build(b: *Builder) !void { |
| 54 | if (!only_install_lib_files) { | 55 | if (!only_install_lib_files) { |
| 55 | var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); | 56 | var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); |
| 56 | exe.setBuildMode(mode); | 57 | exe.setBuildMode(mode); |
| 58 | exe.setTarget(target); | ||
| 57 | test_step.dependOn(&exe.step); | 59 | test_step.dependOn(&exe.step); |
| 58 | b.default_step.dependOn(&exe.step); | 60 | b.default_step.dependOn(&exe.step); |
| 59 | 61 |
src-self-hosted/Module.zig+109-28| ... | @@ -2352,6 +2352,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In | ... | @@ -2352,6 +2352,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In |
| 2352 | .fntype => return self.analyzeInstFnType(scope, old_inst.castTag(.fntype).?), | 2352 | .fntype => return self.analyzeInstFnType(scope, old_inst.castTag(.fntype).?), |
| 2353 | .intcast => return self.analyzeInstIntCast(scope, old_inst.castTag(.intcast).?), | 2353 | .intcast => return self.analyzeInstIntCast(scope, old_inst.castTag(.intcast).?), |
| 2354 | .bitcast => return self.analyzeInstBitCast(scope, old_inst.castTag(.bitcast).?), | 2354 | .bitcast => return self.analyzeInstBitCast(scope, old_inst.castTag(.bitcast).?), |
| 2355 | .floatcast => return self.analyzeInstFloatCast(scope, old_inst.castTag(.floatcast).?), | ||
| 2355 | .elemptr => return self.analyzeInstElemPtr(scope, old_inst.castTag(.elemptr).?), | 2356 | .elemptr => return self.analyzeInstElemPtr(scope, old_inst.castTag(.elemptr).?), |
| 2356 | .add => return self.analyzeInstAdd(scope, old_inst.castTag(.add).?), | 2357 | .add => return self.analyzeInstAdd(scope, old_inst.castTag(.add).?), |
| 2357 | .sub => return self.analyzeInstSub(scope, old_inst.castTag(.sub).?), | 2358 | .sub => return self.analyzeInstSub(scope, old_inst.castTag(.sub).?), |
| ... | @@ -2796,16 +2797,16 @@ fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPt | ... | @@ -2796,16 +2797,16 @@ fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPt |
| 2796 | } | 2797 | } |
| 2797 | } | 2798 | } |
| 2798 | 2799 | ||
| 2799 | fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *zir.Inst.IntCast) InnerError!*Inst { | 2800 | fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.IntCast) InnerError!*Inst { |
| 2800 | const dest_type = try self.resolveType(scope, intcast.positionals.dest_type); | 2801 | const dest_type = try self.resolveType(scope, inst.positionals.dest_type); |
| 2801 | const new_inst = try self.resolveInst(scope, intcast.positionals.value); | 2802 | const operand = try self.resolveInst(scope, inst.positionals.operand); |
| 2802 | 2803 | ||
| 2803 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { | 2804 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { |
| 2804 | .ComptimeInt => true, | 2805 | .ComptimeInt => true, |
| 2805 | .Int => false, | 2806 | .Int => false, |
| 2806 | else => return self.fail( | 2807 | else => return self.fail( |
| 2807 | scope, | 2808 | scope, |
| 2808 | intcast.positionals.dest_type.src, | 2809 | inst.positionals.dest_type.src, |
| 2809 | "expected integer type, found '{}'", | 2810 | "expected integer type, found '{}'", |
| 2810 | .{ | 2811 | .{ |
| 2811 | dest_type, | 2812 | dest_type, |
| ... | @@ -2813,21 +2814,23 @@ fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *zir.Inst.IntCast) | ... | @@ -2813,21 +2814,23 @@ fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *zir.Inst.IntCast) |
| 2813 | ), | 2814 | ), |
| 2814 | }; | 2815 | }; |
| 2815 | 2816 | ||
| 2816 | switch (new_inst.ty.zigTypeTag()) { | 2817 | switch (operand.ty.zigTypeTag()) { |
| 2817 | .ComptimeInt, .Int => {}, | 2818 | .ComptimeInt, .Int => {}, |
| 2818 | else => return self.fail( | 2819 | else => return self.fail( |
| 2819 | scope, | 2820 | scope, |
| 2820 | intcast.positionals.value.src, | 2821 | inst.positionals.operand.src, |
| 2821 | "expected integer type, found '{}'", | 2822 | "expected integer type, found '{}'", |
| 2822 | .{new_inst.ty}, | 2823 | .{operand.ty}, |
| 2823 | ), | 2824 | ), |
| 2824 | } | 2825 | } |
| 2825 | 2826 | ||
| 2826 | if (dest_is_comptime_int or new_inst.value() != null) { | 2827 | if (operand.value() != null) { |
| 2827 | return self.coerce(scope, dest_type, new_inst); | 2828 | return self.coerce(scope, dest_type, operand); |
| 2829 | } else if (dest_is_comptime_int) { | ||
| 2830 | return self.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{}); | ||
| 2828 | } | 2831 | } |
| 2829 | 2832 | ||
| 2830 | return self.fail(scope, intcast.base.src, "TODO implement analyze widen or shorten int", .{}); | 2833 | return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{}); |
| 2831 | } | 2834 | } |
| 2832 | 2835 | ||
| 2833 | fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BitCast) InnerError!*Inst { | 2836 | 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 | ... | @@ -2836,6 +2839,42 @@ fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BitCast) Inn |
| 2836 | return self.bitcast(scope, dest_type, operand); | 2839 | return self.bitcast(scope, dest_type, operand); |
| 2837 | } | 2840 | } |
| 2838 | 2841 | ||
| 2842 | fn analyzeInstFloatCast(self: *Module, scope: *Scope, inst: *zir.Inst.FloatCast) InnerError!*Inst { | ||
| 2843 | const dest_type = try self.resolveType(scope, inst.positionals.dest_type); | ||
| 2844 | const operand = try self.resolveInst(scope, inst.positionals.operand); | ||
| 2845 | |||
| 2846 | const dest_is_comptime_float = switch (dest_type.zigTypeTag()) { | ||
| 2847 | .ComptimeFloat => true, | ||
| 2848 | .Float => false, | ||
| 2849 | else => return self.fail( | ||
| 2850 | scope, | ||
| 2851 | inst.positionals.dest_type.src, | ||
| 2852 | "expected float type, found '{}'", | ||
| 2853 | .{ | ||
| 2854 | dest_type, | ||
| 2855 | }, | ||
| 2856 | ), | ||
| 2857 | }; | ||
| 2858 | |||
| 2859 | switch (operand.ty.zigTypeTag()) { | ||
| 2860 | .ComptimeFloat, .Float, .ComptimeInt => {}, | ||
| 2861 | else => return self.fail( | ||
| 2862 | scope, | ||
| 2863 | inst.positionals.operand.src, | ||
| 2864 | "expected float type, found '{}'", | ||
| 2865 | .{operand.ty}, | ||
| 2866 | ), | ||
| 2867 | } | ||
| 2868 | |||
| 2869 | if (operand.value() != null) { | ||
| 2870 | return self.coerce(scope, dest_type, operand); | ||
| 2871 | } else if (dest_is_comptime_float) { | ||
| 2872 | return self.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{}); | ||
| 2873 | } | ||
| 2874 | |||
| 2875 | return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{}); | ||
| 2876 | } | ||
| 2877 | |||
| 2839 | fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst { | 2878 | fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst { |
| 2840 | const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr); | 2879 | const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr); |
| 2841 | const uncasted_index = try self.resolveInst(scope, inst.positionals.index); | 2880 | const uncasted_index = try self.resolveInst(scope, inst.positionals.index); |
| ... | @@ -3358,6 +3397,14 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | ... | @@ -3358,6 +3397,14 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { |
| 3358 | return self.bitcast(scope, dest_type, inst); | 3397 | return self.bitcast(scope, dest_type, inst); |
| 3359 | } | 3398 | } |
| 3360 | 3399 | ||
| 3400 | // undefined to anything | ||
| 3401 | if (inst.value()) |val| { | ||
| 3402 | if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) { | ||
| 3403 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3404 | } | ||
| 3405 | } | ||
| 3406 | assert(inst.ty.zigTypeTag() != .Undefined); | ||
| 3407 | |||
| 3361 | // *[N]T to []T | 3408 | // *[N]T to []T |
| 3362 | if (inst.ty.isSinglePointer() and dest_type.isSlice() and | 3409 | if (inst.ty.isSinglePointer() and dest_type.isSlice() and |
| 3363 | (!inst.ty.pointerIsConst() or dest_type.pointerIsConst())) | 3410 | (!inst.ty.pointerIsConst() or dest_type.pointerIsConst())) |
| ... | @@ -3371,31 +3418,65 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | ... | @@ -3371,31 +3418,65 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { |
| 3371 | } | 3418 | } |
| 3372 | } | 3419 | } |
| 3373 | 3420 | ||
| 3374 | // comptime_int to fixed-width integer | 3421 | // comptime known number to other number |
| 3375 | if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) { | 3422 | if (inst.value()) |val| { |
| 3376 | // The representation is already correct; we only need to make sure it fits in the destination type. | 3423 | const src_zig_tag = inst.ty.zigTypeTag(); |
| 3377 | const val = inst.value().?; // comptime_int always has comptime known value | 3424 | const dst_zig_tag = dest_type.zigTypeTag(); |
| 3378 | if (!val.intFitsInType(dest_type, self.target())) { | 3425 | |
| 3379 | return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); | 3426 | if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) { |
| 3427 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | ||
| 3428 | if (val.floatHasFraction()) { | ||
| 3429 | return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty }); | ||
| 3430 | } | ||
| 3431 | return self.fail(scope, inst.src, "TODO float to int", .{}); | ||
| 3432 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | ||
| 3433 | if (!val.intFitsInType(dest_type, self.target())) { | ||
| 3434 | return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); | ||
| 3435 | } | ||
| 3436 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3437 | } | ||
| 3438 | } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { | ||
| 3439 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | ||
| 3440 | const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) { | ||
| 3441 | error.Overflow => return self.fail( | ||
| 3442 | scope, | ||
| 3443 | inst.src, | ||
| 3444 | "cast of value {} to type '{}' loses information", | ||
| 3445 | .{ val, dest_type }, | ||
| 3446 | ), | ||
| 3447 | error.OutOfMemory => return error.OutOfMemory, | ||
| 3448 | }; | ||
| 3449 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res }); | ||
| 3450 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | ||
| 3451 | return self.fail(scope, inst.src, "TODO int to float", .{}); | ||
| 3452 | } | ||
| 3380 | } | 3453 | } |
| 3381 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3382 | } | 3454 | } |
| 3383 | 3455 | ||
| 3384 | // integer widening | 3456 | // integer widening |
| 3385 | if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { | 3457 | if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { |
| 3458 | assert(inst.value() == null); // handled above | ||
| 3459 | |||
| 3386 | const src_info = inst.ty.intInfo(self.target()); | 3460 | const src_info = inst.ty.intInfo(self.target()); |
| 3387 | const dst_info = dest_type.intInfo(self.target()); | 3461 | const dst_info = dest_type.intInfo(self.target()); |
| 3388 | if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) { | 3462 | if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or |
| 3389 | if (inst.value()) |val| { | 3463 | // small enough unsigned ints can get casted to large enough signed ints |
| 3390 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | 3464 | (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits)) |
| 3391 | } else { | 3465 | { |
| 3392 | return self.fail(scope, inst.src, "TODO implement runtime integer widening ({} to {})", .{ | 3466 | const b = try self.requireRuntimeBlock(scope, inst.src); |
| 3393 | inst.ty, | 3467 | return self.addUnOp(b, inst.src, dest_type, .intcast, inst); |
| 3394 | dest_type, | 3468 | } |
| 3395 | }); | 3469 | } |
| 3396 | } | 3470 | |
| 3397 | } else { | 3471 | // float widening |
| 3398 | return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type }); | 3472 | if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) { |
| 3473 | assert(inst.value() == null); // handled above | ||
| 3474 | |||
| 3475 | const src_bits = inst.ty.floatBits(self.target()); | ||
| 3476 | const dst_bits = dest_type.floatBits(self.target()); | ||
| 3477 | if (dst_bits >= src_bits) { | ||
| 3478 | const b = try self.requireRuntimeBlock(scope, inst.src); | ||
| 3479 | return self.addUnOp(b, inst.src, dest_type, .floatcast, inst); | ||
| 3399 | } | 3480 | } |
| 3400 | } | 3481 | } |
| 3401 | 3482 |
src-self-hosted/astgen.zig+56-25| ... | @@ -38,6 +38,8 @@ pub fn expr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { | ... | @@ -38,6 +38,8 @@ pub fn expr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { |
| 38 | .Period => return field(mod, scope, node.castTag(.Period).?), | 38 | .Period => return field(mod, scope, node.castTag(.Period).?), |
| 39 | .Deref => return deref(mod, scope, node.castTag(.Deref).?), | 39 | .Deref => return deref(mod, scope, node.castTag(.Deref).?), |
| 40 | .BoolNot => return boolNot(mod, scope, node.castTag(.BoolNot).?), | 40 | .BoolNot => return boolNot(mod, scope, node.castTag(.BoolNot).?), |
| 41 | .FloatLiteral => return floatLiteral(mod, scope, node.castTag(.FloatLiteral).?), | ||
| 42 | .UndefinedLiteral, .BoolLiteral, .NullLiteral => return primitiveLiteral(mod, scope, node), | ||
| 41 | else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}), | 43 | else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}), |
| 42 | } | 44 | } |
| 43 | } | 45 | } |
| ... | @@ -323,7 +325,14 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerErr | ... | @@ -323,7 +325,14 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerErr |
| 323 | 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type), | 325 | 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type), |
| 324 | 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type), | 326 | 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type), |
| 325 | 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type), | 327 | 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type), |
| 326 | else => return mod.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}), | 328 | else => { |
| 329 | const int_type_payload = try scope.arena().create(Value.Payload.IntType); | ||
| 330 | int_type_payload.* = .{ .signed = is_signed, .bits = bit_count }; | ||
| 331 | return mod.addZIRInstConst(scope, src, .{ | ||
| 332 | .ty = Type.initTag(.comptime_int), | ||
| 333 | .val = Value.initPayload(&int_type_payload.base), | ||
| 334 | }); | ||
| 335 | }, | ||
| 327 | }; | 336 | }; |
| 328 | return mod.addZIRInstConst(scope, src, .{ | 337 | return mod.addZIRInstConst(scope, src, .{ |
| 329 | .ty = Type.initTag(.type), | 338 | .ty = Type.initTag(.type), |
| ... | @@ -405,6 +414,52 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral | ... | @@ -405,6 +414,52 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral |
| 405 | } | 414 | } |
| 406 | } | 415 | } |
| 407 | 416 | ||
| 417 | fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.FloatLiteral) InnerError!*zir.Inst { | ||
| 418 | const arena = scope.arena(); | ||
| 419 | const tree = scope.tree(); | ||
| 420 | const bytes = tree.tokenSlice(float_lit.token); | ||
| 421 | if (bytes.len > 2 and bytes[1] == 'x') { | ||
| 422 | return mod.failTok(scope, float_lit.token, "TODO hex floats", .{}); | ||
| 423 | } | ||
| 424 | |||
| 425 | const val = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) { | ||
| 426 | error.InvalidCharacter => unreachable, // validated by tokenizer | ||
| 427 | }; | ||
| 428 | const float_payload = try arena.create(Value.Payload.Float_128); | ||
| 429 | float_payload.* = .{ .val = val }; | ||
| 430 | const src = tree.token_locs[float_lit.token].start; | ||
| 431 | return mod.addZIRInstConst(scope, src, .{ | ||
| 432 | .ty = Type.initTag(.comptime_float), | ||
| 433 | .val = Value.initPayload(&float_payload.base), | ||
| 434 | }); | ||
| 435 | } | ||
| 436 | |||
| 437 | fn primitiveLiteral(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { | ||
| 438 | const arena = scope.arena(); | ||
| 439 | const tree = scope.tree(); | ||
| 440 | const src = tree.token_locs[node.firstToken()].start; | ||
| 441 | |||
| 442 | if (node.cast(ast.Node.BoolLiteral)) |bool_node| { | ||
| 443 | return mod.addZIRInstConst(scope, src, .{ | ||
| 444 | .ty = Type.initTag(.bool), | ||
| 445 | .val = if (tree.token_ids[bool_node.token] == .Keyword_true) | ||
| 446 | Value.initTag(.bool_true) | ||
| 447 | else | ||
| 448 | Value.initTag(.bool_false), | ||
| 449 | }); | ||
| 450 | } else if (node.tag == .UndefinedLiteral) { | ||
| 451 | return mod.addZIRInstConst(scope, src, .{ | ||
| 452 | .ty = Type.initTag(.@"undefined"), | ||
| 453 | .val = Value.initTag(.undef), | ||
| 454 | }); | ||
| 455 | } else if (node.tag == .NullLiteral) { | ||
| 456 | return mod.addZIRInstConst(scope, src, .{ | ||
| 457 | .ty = Type.initTag(.@"null"), | ||
| 458 | .val = Value.initTag(.null_value), | ||
| 459 | }); | ||
| 460 | } else unreachable; | ||
| 461 | } | ||
| 462 | |||
| 408 | fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst { | 463 | fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst { |
| 409 | if (asm_node.outputs.len != 0) { | 464 | if (asm_node.outputs.len != 0) { |
| 410 | return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{}); | 465 | return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{}); |
| ... | @@ -534,30 +589,6 @@ fn getSimplePrimitiveValue(name: []const u8) ?TypedValue { | ... | @@ -534,30 +589,6 @@ fn getSimplePrimitiveValue(name: []const u8) ?TypedValue { |
| 534 | .val = Value.initTag(tag), | 589 | .val = Value.initTag(tag), |
| 535 | }; | 590 | }; |
| 536 | } | 591 | } |
| 537 | if (mem.eql(u8, name, "null")) { | ||
| 538 | return TypedValue{ | ||
| 539 | .ty = Type.initTag(.@"null"), | ||
| 540 | .val = Value.initTag(.null_value), | ||
| 541 | }; | ||
| 542 | } | ||
| 543 | if (mem.eql(u8, name, "undefined")) { | ||
| 544 | return TypedValue{ | ||
| 545 | .ty = Type.initTag(.@"undefined"), | ||
| 546 | .val = Value.initTag(.undef), | ||
| 547 | }; | ||
| 548 | } | ||
| 549 | if (mem.eql(u8, name, "true")) { | ||
| 550 | return TypedValue{ | ||
| 551 | .ty = Type.initTag(.bool), | ||
| 552 | .val = Value.initTag(.bool_true), | ||
| 553 | }; | ||
| 554 | } | ||
| 555 | if (mem.eql(u8, name, "false")) { | ||
| 556 | return TypedValue{ | ||
| 557 | .ty = Type.initTag(.bool), | ||
| 558 | .val = Value.initTag(.bool_false), | ||
| 559 | }; | ||
| 560 | } | ||
| 561 | return null; | 592 | return null; |
| 562 | } | 593 | } |
| 563 | 594 |
src-self-hosted/codegen.zig+20| ... | @@ -459,6 +459,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -459,6 +459,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 459 | .sub => return self.genSub(inst.castTag(.sub).?), | 459 | .sub => return self.genSub(inst.castTag(.sub).?), |
| 460 | .unreach => return MCValue{ .unreach = {} }, | 460 | .unreach => return MCValue{ .unreach = {} }, |
| 461 | .not => return self.genNot(inst.castTag(.not).?), | 461 | .not => return self.genNot(inst.castTag(.not).?), |
| 462 | .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?), | ||
| 463 | .intcast => return self.genIntCast(inst.castTag(.intcast).?), | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | ||
| 468 | // No side effects, so if it's unreferenced, do nothing. | ||
| 469 | if (inst.base.isUnused()) | ||
| 470 | return MCValue.dead; | ||
| 471 | switch (arch) { | ||
| 472 | else => return self.fail(inst.base.src, "TODO implement floatCast for {}", .{self.target.cpu.arch}), | ||
| 473 | } | ||
| 474 | } | ||
| 475 | |||
| 476 | fn genIntCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | ||
| 477 | // No side effects, so if it's unreferenced, do nothing. | ||
| 478 | if (inst.base.isUnused()) | ||
| 479 | return MCValue.dead; | ||
| 480 | switch (arch) { | ||
| 481 | else => return self.fail(inst.base.src, "TODO implement intCast for {}", .{self.target.cpu.arch}), | ||
| 462 | } | 482 | } |
| 463 | } | 483 | } |
| 464 | 484 |
src-self-hosted/ir.zig+4| ... | @@ -71,6 +71,8 @@ pub const Inst = struct { | ... | @@ -71,6 +71,8 @@ pub const Inst = struct { |
| 71 | sub, | 71 | sub, |
| 72 | unreach, | 72 | unreach, |
| 73 | not, | 73 | not, |
| 74 | floatcast, | ||
| 75 | intcast, | ||
| 74 | 76 | ||
| 75 | /// There is one-to-one correspondence between tag and type for now, | 77 | /// There is one-to-one correspondence between tag and type for now, |
| 76 | /// but this will not always be the case. For example, binary operations | 78 | /// but this will not always be the case. For example, binary operations |
| ... | @@ -89,6 +91,8 @@ pub const Inst = struct { | ... | @@ -89,6 +91,8 @@ pub const Inst = struct { |
| 89 | .isnonnull, | 91 | .isnonnull, |
| 90 | .isnull, | 92 | .isnull, |
| 91 | .ptrtoint, | 93 | .ptrtoint, |
| 94 | .floatcast, | ||
| 95 | .intcast, | ||
| 92 | => UnOp, | 96 | => UnOp, |
| 93 | 97 | ||
| 94 | .add, | 98 | .add, |
src-self-hosted/value.zig+211-18| ... | @@ -70,6 +70,7 @@ pub const Value = extern union { | ... | @@ -70,6 +70,7 @@ pub const Value = extern union { |
| 70 | // After this, the tag requires a payload. | 70 | // After this, the tag requires a payload. |
| 71 | 71 | ||
| 72 | ty, | 72 | ty, |
| 73 | int_type, | ||
| 73 | int_u64, | 74 | int_u64, |
| 74 | int_i64, | 75 | int_i64, |
| 75 | int_big_positive, | 76 | int_big_positive, |
| ... | @@ -80,6 +81,10 @@ pub const Value = extern union { | ... | @@ -80,6 +81,10 @@ pub const Value = extern union { |
| 80 | elem_ptr, | 81 | elem_ptr, |
| 81 | bytes, | 82 | bytes, |
| 82 | repeated, // the value is a value repeated some number of times | 83 | repeated, // the value is a value repeated some number of times |
| 84 | float_16, | ||
| 85 | float_32, | ||
| 86 | float_64, | ||
| 87 | float_128, | ||
| 83 | 88 | ||
| 84 | pub const last_no_payload_tag = Tag.bool_false; | 89 | pub const last_no_payload_tag = Tag.bool_false; |
| 85 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | 90 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | @@ -174,6 +179,7 @@ pub const Value = extern union { | ... | @@ -174,6 +179,7 @@ pub const Value = extern union { |
| 174 | }; | 179 | }; |
| 175 | return Value{ .ptr_otherwise = &new_payload.base }; | 180 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 176 | }, | 181 | }, |
| 182 | .int_type => return self.copyPayloadShallow(allocator, Payload.IntType), | ||
| 177 | .int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64), | 183 | .int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64), |
| 178 | .int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64), | 184 | .int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64), |
| 179 | .int_big_positive => { | 185 | .int_big_positive => { |
| ... | @@ -213,6 +219,10 @@ pub const Value = extern union { | ... | @@ -213,6 +219,10 @@ pub const Value = extern union { |
| 213 | }; | 219 | }; |
| 214 | return Value{ .ptr_otherwise = &new_payload.base }; | 220 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 215 | }, | 221 | }, |
| 222 | .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16), | ||
| 223 | .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32), | ||
| 224 | .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64), | ||
| 225 | .float_128 => return self.copyPayloadShallow(allocator, Payload.Float_128), | ||
| 216 | } | 226 | } |
| 217 | } | 227 | } |
| 218 | 228 | ||
| ... | @@ -279,6 +289,13 @@ pub const Value = extern union { | ... | @@ -279,6 +289,13 @@ pub const Value = extern union { |
| 279 | .bool_true => return out_stream.writeAll("true"), | 289 | .bool_true => return out_stream.writeAll("true"), |
| 280 | .bool_false => return out_stream.writeAll("false"), | 290 | .bool_false => return out_stream.writeAll("false"), |
| 281 | .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream), | 291 | .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream), |
| 292 | .int_type => { | ||
| 293 | const int_type = val.cast(Payload.IntType).?; | ||
| 294 | return out_stream.print("{}{}", .{ | ||
| 295 | if (int_type.signed) "s" else "u", | ||
| 296 | int_type.bits, | ||
| 297 | }); | ||
| 298 | }, | ||
| 282 | .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream), | 299 | .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream), |
| 283 | .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream), | 300 | .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream), |
| 284 | .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}), | 301 | .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}), |
| ... | @@ -300,6 +317,10 @@ pub const Value = extern union { | ... | @@ -300,6 +317,10 @@ pub const Value = extern union { |
| 300 | try out_stream.writeAll("(repeated) "); | 317 | try out_stream.writeAll("(repeated) "); |
| 301 | val = val.cast(Payload.Repeated).?.val; | 318 | val = val.cast(Payload.Repeated).?.val; |
| 302 | }, | 319 | }, |
| 320 | .float_16 => return out_stream.print("{}", .{val.cast(Payload.Float_16).?.val}), | ||
| 321 | .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}), | ||
| 322 | .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}), | ||
| 323 | .float_128 => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}), | ||
| 303 | }; | 324 | }; |
| 304 | } | 325 | } |
| 305 | 326 | ||
| ... | @@ -323,6 +344,7 @@ pub const Value = extern union { | ... | @@ -323,6 +344,7 @@ pub const Value = extern union { |
| 323 | pub fn toType(self: Value) Type { | 344 | pub fn toType(self: Value) Type { |
| 324 | return switch (self.tag()) { | 345 | return switch (self.tag()) { |
| 325 | .ty => self.cast(Payload.Ty).?.ty, | 346 | .ty => self.cast(Payload.Ty).?.ty, |
| 347 | .int_type => @panic("TODO int type to type"), | ||
| 326 | 348 | ||
| 327 | .u8_type => Type.initTag(.u8), | 349 | .u8_type => Type.initTag(.u8), |
| 328 | .i8_type => Type.initTag(.i8), | 350 | .i8_type => Type.initTag(.i8), |
| ... | @@ -380,6 +402,10 @@ pub const Value = extern union { | ... | @@ -380,6 +402,10 @@ pub const Value = extern union { |
| 380 | .elem_ptr, | 402 | .elem_ptr, |
| 381 | .bytes, | 403 | .bytes, |
| 382 | .repeated, | 404 | .repeated, |
| 405 | .float_16, | ||
| 406 | .float_32, | ||
| 407 | .float_64, | ||
| 408 | .float_128, | ||
| 383 | => unreachable, | 409 | => unreachable, |
| 384 | }; | 410 | }; |
| 385 | } | 411 | } |
| ... | @@ -388,6 +414,7 @@ pub const Value = extern union { | ... | @@ -388,6 +414,7 @@ pub const Value = extern union { |
| 388 | pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst { | 414 | pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst { |
| 389 | switch (self.tag()) { | 415 | switch (self.tag()) { |
| 390 | .ty, | 416 | .ty, |
| 417 | .int_type, | ||
| 391 | .u8_type, | 418 | .u8_type, |
| 392 | .i8_type, | 419 | .i8_type, |
| 393 | .u16_type, | 420 | .u16_type, |
| ... | @@ -435,6 +462,10 @@ pub const Value = extern union { | ... | @@ -435,6 +462,10 @@ pub const Value = extern union { |
| 435 | .bytes, | 462 | .bytes, |
| 436 | .undef, | 463 | .undef, |
| 437 | .repeated, | 464 | .repeated, |
| 465 | .float_16, | ||
| 466 | .float_32, | ||
| 467 | .float_64, | ||
| 468 | .float_128, | ||
| 438 | => unreachable, | 469 | => unreachable, |
| 439 | 470 | ||
| 440 | .the_one_possible_value, // An integer with one possible value is always zero. | 471 | .the_one_possible_value, // An integer with one possible value is always zero. |
| ... | @@ -455,6 +486,7 @@ pub const Value = extern union { | ... | @@ -455,6 +486,7 @@ pub const Value = extern union { |
| 455 | pub fn toUnsignedInt(self: Value) u64 { | 486 | pub fn toUnsignedInt(self: Value) u64 { |
| 456 | switch (self.tag()) { | 487 | switch (self.tag()) { |
| 457 | .ty, | 488 | .ty, |
| 489 | .int_type, | ||
| 458 | .u8_type, | 490 | .u8_type, |
| 459 | .i8_type, | 491 | .i8_type, |
| 460 | .u16_type, | 492 | .u16_type, |
| ... | @@ -502,6 +534,10 @@ pub const Value = extern union { | ... | @@ -502,6 +534,10 @@ pub const Value = extern union { |
| 502 | .bytes, | 534 | .bytes, |
| 503 | .undef, | 535 | .undef, |
| 504 | .repeated, | 536 | .repeated, |
| 537 | .float_16, | ||
| 538 | .float_32, | ||
| 539 | .float_64, | ||
| 540 | .float_128, | ||
| 505 | => unreachable, | 541 | => unreachable, |
| 506 | 542 | ||
| 507 | .zero, | 543 | .zero, |
| ... | @@ -518,11 +554,38 @@ pub const Value = extern union { | ... | @@ -518,11 +554,38 @@ pub const Value = extern union { |
| 518 | } | 554 | } |
| 519 | } | 555 | } |
| 520 | 556 | ||
| 557 | pub fn toBool(self: Value) bool { | ||
| 558 | return switch (self.tag()) { | ||
| 559 | .bool_true => true, | ||
| 560 | .bool_false, .zero => false, | ||
| 561 | else => unreachable, | ||
| 562 | }; | ||
| 563 | } | ||
| 564 | |||
| 565 | /// Asserts that the value is a float or an integer. | ||
| 566 | pub fn toFloat(self: Value, comptime T: type) T { | ||
| 567 | return switch (self.tag()) { | ||
| 568 | .float_16 => @panic("TODO soft float"), | ||
| 569 | .float_32 => @floatCast(T, self.cast(Payload.Float_32).?.val), | ||
| 570 | .float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val), | ||
| 571 | .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val), | ||
| 572 | |||
| 573 | .zero, .the_one_possible_value => 0, | ||
| 574 | .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int), | ||
| 575 | // .int_i64 => @intToFloat(f128, self.cast(Payload.Int_i64).?.int), | ||
| 576 | .int_i64 => @panic("TODO lld: error: undefined symbol: __floatditf"), | ||
| 577 | |||
| 578 | .int_big_positive, .int_big_negative => @panic("big int to f128"), | ||
| 579 | else => unreachable, | ||
| 580 | }; | ||
| 581 | } | ||
| 582 | |||
| 521 | /// Asserts the value is an integer and not undefined. | 583 | /// Asserts the value is an integer and not undefined. |
| 522 | /// Returns the number of bits the value requires to represent stored in twos complement form. | 584 | /// Returns the number of bits the value requires to represent stored in twos complement form. |
| 523 | pub fn intBitCountTwosComp(self: Value) usize { | 585 | pub fn intBitCountTwosComp(self: Value) usize { |
| 524 | switch (self.tag()) { | 586 | switch (self.tag()) { |
| 525 | .ty, | 587 | .ty, |
| 588 | .int_type, | ||
| 526 | .u8_type, | 589 | .u8_type, |
| 527 | .i8_type, | 590 | .i8_type, |
| 528 | .u16_type, | 591 | .u16_type, |
| ... | @@ -570,6 +633,10 @@ pub const Value = extern union { | ... | @@ -570,6 +633,10 @@ pub const Value = extern union { |
| 570 | .bytes, | 633 | .bytes, |
| 571 | .undef, | 634 | .undef, |
| 572 | .repeated, | 635 | .repeated, |
| 636 | .float_16, | ||
| 637 | .float_32, | ||
| 638 | .float_64, | ||
| 639 | .float_128, | ||
| 573 | => unreachable, | 640 | => unreachable, |
| 574 | 641 | ||
| 575 | .the_one_possible_value, // an integer with one possible value is always zero | 642 | .the_one_possible_value, // an integer with one possible value is always zero |
| ... | @@ -596,6 +663,7 @@ pub const Value = extern union { | ... | @@ -596,6 +663,7 @@ pub const Value = extern union { |
| 596 | pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { | 663 | pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { |
| 597 | switch (self.tag()) { | 664 | switch (self.tag()) { |
| 598 | .ty, | 665 | .ty, |
| 666 | .int_type, | ||
| 599 | .u8_type, | 667 | .u8_type, |
| 600 | .i8_type, | 668 | .i8_type, |
| 601 | .u16_type, | 669 | .u16_type, |
| ... | @@ -642,6 +710,10 @@ pub const Value = extern union { | ... | @@ -642,6 +710,10 @@ pub const Value = extern union { |
| 642 | .elem_ptr, | 710 | .elem_ptr, |
| 643 | .bytes, | 711 | .bytes, |
| 644 | .repeated, | 712 | .repeated, |
| 713 | .float_16, | ||
| 714 | .float_32, | ||
| 715 | .float_64, | ||
| 716 | .float_128, | ||
| 645 | => unreachable, | 717 | => unreachable, |
| 646 | 718 | ||
| 647 | .zero, | 719 | .zero, |
| ... | @@ -701,10 +773,55 @@ pub const Value = extern union { | ... | @@ -701,10 +773,55 @@ pub const Value = extern union { |
| 701 | } | 773 | } |
| 702 | } | 774 | } |
| 703 | 775 | ||
| 776 | /// Converts an integer or a float to a float. | ||
| 777 | /// Returns `error.Overflow` if the value does not fit in the new type. | ||
| 778 | pub fn floatCast(self: Value, allocator: *Allocator, ty: Type, target: Target) !Value { | ||
| 779 | const dest_bit_count = switch (ty.tag()) { | ||
| 780 | .comptime_float => 128, | ||
| 781 | else => ty.floatBits(target), | ||
| 782 | }; | ||
| 783 | switch (dest_bit_count) { | ||
| 784 | 16, 32, 64, 128 => {}, | ||
| 785 | else => std.debug.panic("TODO float cast bit count {}\n", .{dest_bit_count}), | ||
| 786 | } | ||
| 787 | if (ty.isInt()) { | ||
| 788 | @panic("TODO int to float"); | ||
| 789 | } | ||
| 790 | |||
| 791 | switch (dest_bit_count) { | ||
| 792 | 16 => { | ||
| 793 | @panic("TODO soft float"); | ||
| 794 | // var res_payload = Value.Payload.Float_16{.val = self.toFloat(f16)}; | ||
| 795 | // if (!self.eql(Value.initPayload(&res_payload.base))) | ||
| 796 | // return error.Overflow; | ||
| 797 | // return Value.initPayload(&res_payload.base).copy(allocator); | ||
| 798 | }, | ||
| 799 | 32 => { | ||
| 800 | var res_payload = Value.Payload.Float_32{.val = self.toFloat(f32)}; | ||
| 801 | if (!self.eql(Value.initPayload(&res_payload.base))) | ||
| 802 | return error.Overflow; | ||
| 803 | return Value.initPayload(&res_payload.base).copy(allocator); | ||
| 804 | }, | ||
| 805 | 64 => { | ||
| 806 | var res_payload = Value.Payload.Float_64{.val = self.toFloat(f64)}; | ||
| 807 | if (!self.eql(Value.initPayload(&res_payload.base))) | ||
| 808 | return error.Overflow; | ||
| 809 | return Value.initPayload(&res_payload.base).copy(allocator); | ||
| 810 | }, | ||
| 811 | 128 => { | ||
| 812 | const float_payload = try allocator.create(Value.Payload.Float_128); | ||
| 813 | float_payload.* = .{ .val = self.toFloat(f128) }; | ||
| 814 | return Value.initPayload(&float_payload.base); | ||
| 815 | }, | ||
| 816 | else => unreachable, | ||
| 817 | } | ||
| 818 | } | ||
| 819 | |||
| 704 | /// Asserts the value is a float | 820 | /// Asserts the value is a float |
| 705 | pub fn floatHasFraction(self: Value) bool { | 821 | pub fn floatHasFraction(self: Value) bool { |
| 706 | return switch (self.tag()) { | 822 | return switch (self.tag()) { |
| 707 | .ty, | 823 | .ty, |
| 824 | .int_type, | ||
| 708 | .u8_type, | 825 | .u8_type, |
| 709 | .i8_type, | 826 | .i8_type, |
| 710 | .u16_type, | 827 | .u16_type, |
| ... | @@ -762,12 +879,19 @@ pub const Value = extern union { | ... | @@ -762,12 +879,19 @@ pub const Value = extern union { |
| 762 | => unreachable, | 879 | => unreachable, |
| 763 | 880 | ||
| 764 | .zero => false, | 881 | .zero => false, |
| 882 | |||
| 883 | .float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0, | ||
| 884 | .float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0, | ||
| 885 | .float_64 => @rem(self.cast(Payload.Float_64).?.val, 1) != 0, | ||
| 886 | // .float_128 => @rem(self.cast(Payload.Float_128).?.val, 1) != 0, | ||
| 887 | .float_128 => @panic("TODO lld: error: undefined symbol: fmodl"), | ||
| 765 | }; | 888 | }; |
| 766 | } | 889 | } |
| 767 | 890 | ||
| 768 | pub fn orderAgainstZero(lhs: Value) std.math.Order { | 891 | pub fn orderAgainstZero(lhs: Value) std.math.Order { |
| 769 | switch (lhs.tag()) { | 892 | return switch (lhs.tag()) { |
| 770 | .ty, | 893 | .ty, |
| 894 | .int_type, | ||
| 771 | .u8_type, | 895 | .u8_type, |
| 772 | .i8_type, | 896 | .i8_type, |
| 773 | .u16_type, | 897 | .u16_type, |
| ... | @@ -820,27 +944,49 @@ pub const Value = extern union { | ... | @@ -820,27 +944,49 @@ pub const Value = extern union { |
| 820 | .zero, | 944 | .zero, |
| 821 | .the_one_possible_value, // an integer with one possible value is always zero | 945 | .the_one_possible_value, // an integer with one possible value is always zero |
| 822 | .bool_false, | 946 | .bool_false, |
| 823 | => return .eq, | 947 | => .eq, |
| 824 | 948 | ||
| 825 | .bool_true => return .gt, | 949 | .bool_true => .gt, |
| 826 | 950 | ||
| 827 | .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0), | 951 | .int_u64 => std.math.order(lhs.cast(Payload.Int_u64).?.int, 0), |
| 828 | .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0), | 952 | .int_i64 => std.math.order(lhs.cast(Payload.Int_i64).?.int, 0), |
| 829 | .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0), | 953 | .int_big_positive => lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0), |
| 830 | .int_big_negative => return lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0), | 954 | .int_big_negative => lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0), |
| 831 | } | 955 | |
| 956 | .float_16 => std.math.order(lhs.cast(Payload.Float_16).?.val, 0), | ||
| 957 | .float_32 => std.math.order(lhs.cast(Payload.Float_32).?.val, 0), | ||
| 958 | .float_64 => std.math.order(lhs.cast(Payload.Float_64).?.val, 0), | ||
| 959 | .float_128 => std.math.order(lhs.cast(Payload.Float_128).?.val, 0), | ||
| 960 | }; | ||
| 832 | } | 961 | } |
| 833 | 962 | ||
| 834 | /// Asserts the value is comparable. | 963 | /// Asserts the value is comparable. |
| 835 | pub fn order(lhs: Value, rhs: Value) std.math.Order { | 964 | pub fn order(lhs: Value, rhs: Value) std.math.Order { |
| 836 | const lhs_tag = lhs.tag(); | 965 | const lhs_tag = lhs.tag(); |
| 837 | const rhs_tag = lhs.tag(); | 966 | const rhs_tag = rhs.tag(); |
| 838 | const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value; | 967 | const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value; |
| 839 | const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value; | 968 | const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value; |
| 840 | if (lhs_is_zero) return rhs.orderAgainstZero().invert(); | 969 | if (lhs_is_zero) return rhs.orderAgainstZero().invert(); |
| 841 | if (rhs_is_zero) return lhs.orderAgainstZero(); | 970 | if (rhs_is_zero) return lhs.orderAgainstZero(); |
| 842 | 971 | ||
| 843 | // TODO floats | 972 | const lhs_float = lhs.isFloat(); |
| 973 | const rhs_float = rhs.isFloat(); | ||
| 974 | if (lhs_float and rhs_float) { | ||
| 975 | if (lhs_tag == rhs_tag) { | ||
| 976 | return switch (lhs.tag()) { | ||
| 977 | .float_16 => return std.math.order(lhs.cast(Payload.Float_16).?.val, rhs.cast(Payload.Float_16).?.val), | ||
| 978 | .float_32 => return std.math.order(lhs.cast(Payload.Float_32).?.val, rhs.cast(Payload.Float_32).?.val), | ||
| 979 | .float_64 => return std.math.order(lhs.cast(Payload.Float_64).?.val, rhs.cast(Payload.Float_64).?.val), | ||
| 980 | .float_128 => return std.math.order(lhs.cast(Payload.Float_128).?.val, rhs.cast(Payload.Float_128).?.val), | ||
| 981 | else => unreachable, | ||
| 982 | }; | ||
| 983 | } | ||
| 984 | } | ||
| 985 | if (lhs_float or rhs_float) { | ||
| 986 | const lhs_f128 = lhs.toFloat(f128); | ||
| 987 | const rhs_f128 = rhs.toFloat(f128); | ||
| 988 | return std.math.order(lhs_f128, rhs_f128); | ||
| 989 | } | ||
| 844 | 990 | ||
| 845 | var lhs_bigint_space: BigIntSpace = undefined; | 991 | var lhs_bigint_space: BigIntSpace = undefined; |
| 846 | var rhs_bigint_space: BigIntSpace = undefined; | 992 | var rhs_bigint_space: BigIntSpace = undefined; |
| ... | @@ -864,19 +1010,12 @@ pub const Value = extern union { | ... | @@ -864,19 +1010,12 @@ pub const Value = extern union { |
| 864 | return compare(a, .eq, b); | 1010 | return compare(a, .eq, b); |
| 865 | } | 1011 | } |
| 866 | 1012 | ||
| 867 | pub fn toBool(self: Value) bool { | ||
| 868 | return switch (self.tag()) { | ||
| 869 | .bool_true => true, | ||
| 870 | .bool_false, .zero => false, | ||
| 871 | else => unreachable, | ||
| 872 | }; | ||
| 873 | } | ||
| 874 | |||
| 875 | /// Asserts the value is a pointer and dereferences it. | 1013 | /// Asserts the value is a pointer and dereferences it. |
| 876 | /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis. | 1014 | /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis. |
| 877 | pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value { | 1015 | pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value { |
| 878 | return switch (self.tag()) { | 1016 | return switch (self.tag()) { |
| 879 | .ty, | 1017 | .ty, |
| 1018 | .int_type, | ||
| 880 | .u8_type, | 1019 | .u8_type, |
| 881 | .i8_type, | 1020 | .i8_type, |
| 882 | .u16_type, | 1021 | .u16_type, |
| ... | @@ -928,6 +1067,10 @@ pub const Value = extern union { | ... | @@ -928,6 +1067,10 @@ pub const Value = extern union { |
| 928 | .bytes, | 1067 | .bytes, |
| 929 | .undef, | 1068 | .undef, |
| 930 | .repeated, | 1069 | .repeated, |
| 1070 | .float_16, | ||
| 1071 | .float_32, | ||
| 1072 | .float_64, | ||
| 1073 | .float_128, | ||
| 931 | => unreachable, | 1074 | => unreachable, |
| 932 | 1075 | ||
| 933 | .the_one_possible_value => Value.initTag(.the_one_possible_value), | 1076 | .the_one_possible_value => Value.initTag(.the_one_possible_value), |
| ... | @@ -946,6 +1089,7 @@ pub const Value = extern union { | ... | @@ -946,6 +1089,7 @@ pub const Value = extern union { |
| 946 | pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value { | 1089 | pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value { |
| 947 | switch (self.tag()) { | 1090 | switch (self.tag()) { |
| 948 | .ty, | 1091 | .ty, |
| 1092 | .int_type, | ||
| 949 | .u8_type, | 1093 | .u8_type, |
| 950 | .i8_type, | 1094 | .i8_type, |
| 951 | .u16_type, | 1095 | .u16_type, |
| ... | @@ -999,6 +1143,10 @@ pub const Value = extern union { | ... | @@ -999,6 +1143,10 @@ pub const Value = extern union { |
| 999 | .elem_ptr, | 1143 | .elem_ptr, |
| 1000 | .ref_val, | 1144 | .ref_val, |
| 1001 | .decl_ref, | 1145 | .decl_ref, |
| 1146 | .float_16, | ||
| 1147 | .float_32, | ||
| 1148 | .float_64, | ||
| 1149 | .float_128, | ||
| 1002 | => unreachable, | 1150 | => unreachable, |
| 1003 | 1151 | ||
| 1004 | .bytes => { | 1152 | .bytes => { |
| ... | @@ -1032,6 +1180,7 @@ pub const Value = extern union { | ... | @@ -1032,6 +1180,7 @@ pub const Value = extern union { |
| 1032 | pub fn isNull(self: Value) bool { | 1180 | pub fn isNull(self: Value) bool { |
| 1033 | return switch (self.tag()) { | 1181 | return switch (self.tag()) { |
| 1034 | .ty, | 1182 | .ty, |
| 1183 | .int_type, | ||
| 1035 | .u8_type, | 1184 | .u8_type, |
| 1036 | .i8_type, | 1185 | .i8_type, |
| 1037 | .u16_type, | 1186 | .u16_type, |
| ... | @@ -1085,6 +1234,10 @@ pub const Value = extern union { | ... | @@ -1085,6 +1234,10 @@ pub const Value = extern union { |
| 1085 | .elem_ptr, | 1234 | .elem_ptr, |
| 1086 | .bytes, | 1235 | .bytes, |
| 1087 | .repeated, | 1236 | .repeated, |
| 1237 | .float_16, | ||
| 1238 | .float_32, | ||
| 1239 | .float_64, | ||
| 1240 | .float_128, | ||
| 1088 | => false, | 1241 | => false, |
| 1089 | 1242 | ||
| 1090 | .undef => unreachable, | 1243 | .undef => unreachable, |
| ... | @@ -1092,6 +1245,20 @@ pub const Value = extern union { | ... | @@ -1092,6 +1245,20 @@ pub const Value = extern union { |
| 1092 | }; | 1245 | }; |
| 1093 | } | 1246 | } |
| 1094 | 1247 | ||
| 1248 | /// Valid for all types. Asserts the value is not undefined. | ||
| 1249 | pub fn isFloat(self: Value) bool { | ||
| 1250 | return switch (self.tag()) { | ||
| 1251 | .undef => unreachable, | ||
| 1252 | |||
| 1253 | .float_16, | ||
| 1254 | .float_32, | ||
| 1255 | .float_64, | ||
| 1256 | .float_128, | ||
| 1257 | => true, | ||
| 1258 | else => false, | ||
| 1259 | }; | ||
| 1260 | } | ||
| 1261 | |||
| 1095 | /// This type is not copyable since it may contain pointers to its inner data. | 1262 | /// This type is not copyable since it may contain pointers to its inner data. |
| 1096 | pub const Payload = struct { | 1263 | pub const Payload = struct { |
| 1097 | tag: Tag, | 1264 | tag: Tag, |
| ... | @@ -1162,12 +1329,38 @@ pub const Value = extern union { | ... | @@ -1162,12 +1329,38 @@ pub const Value = extern union { |
| 1162 | ty: Type, | 1329 | ty: Type, |
| 1163 | }; | 1330 | }; |
| 1164 | 1331 | ||
| 1332 | pub const IntType = struct { | ||
| 1333 | base: Payload = Payload{ .tag = .int_type }, | ||
| 1334 | bits: u16, | ||
| 1335 | signed: bool, | ||
| 1336 | }; | ||
| 1337 | |||
| 1165 | pub const Repeated = struct { | 1338 | pub const Repeated = struct { |
| 1166 | base: Payload = Payload{ .tag = .ty }, | 1339 | base: Payload = Payload{ .tag = .ty }, |
| 1167 | /// This value is repeated some number of times. The amount of times to repeat | 1340 | /// This value is repeated some number of times. The amount of times to repeat |
| 1168 | /// is stored externally. | 1341 | /// is stored externally. |
| 1169 | val: Value, | 1342 | val: Value, |
| 1170 | }; | 1343 | }; |
| 1344 | |||
| 1345 | pub const Float_16 = struct { | ||
| 1346 | base: Payload = .{ .tag = .float_16 }, | ||
| 1347 | val: f16, | ||
| 1348 | }; | ||
| 1349 | |||
| 1350 | pub const Float_32 = struct { | ||
| 1351 | base: Payload = .{ .tag = .float_32 }, | ||
| 1352 | val: f32, | ||
| 1353 | }; | ||
| 1354 | |||
| 1355 | pub const Float_64 = struct { | ||
| 1356 | base: Payload = .{ .tag = .float_64 }, | ||
| 1357 | val: f64, | ||
| 1358 | }; | ||
| 1359 | |||
| 1360 | pub const Float_128 = struct { | ||
| 1361 | base: Payload = .{ .tag = .float_128 }, | ||
| 1362 | val: f128, | ||
| 1363 | }; | ||
| 1171 | }; | 1364 | }; |
| 1172 | 1365 | ||
| 1173 | /// Big enough to fit any non-BigInt value | 1366 | /// Big enough to fit any non-BigInt value |
src-self-hosted/zir.zig+43-17| ... | @@ -76,6 +76,7 @@ pub const Inst = struct { | ... | @@ -76,6 +76,7 @@ pub const Inst = struct { |
| 76 | primitive, | 76 | primitive, |
| 77 | intcast, | 77 | intcast, |
| 78 | bitcast, | 78 | bitcast, |
| 79 | floatcast, | ||
| 79 | elemptr, | 80 | elemptr, |
| 80 | add, | 81 | add, |
| 81 | sub, | 82 | sub, |
| ... | @@ -137,6 +138,7 @@ pub const Inst = struct { | ... | @@ -137,6 +138,7 @@ pub const Inst = struct { |
| 137 | .fntype => FnType, | 138 | .fntype => FnType, |
| 138 | .intcast => IntCast, | 139 | .intcast => IntCast, |
| 139 | .bitcast => BitCast, | 140 | .bitcast => BitCast, |
| 141 | .floatcast => FloatCast, | ||
| 140 | .elemptr => ElemPtr, | 142 | .elemptr => ElemPtr, |
| 141 | .condbr => CondBr, | 143 | .condbr => CondBr, |
| 142 | }; | 144 | }; |
| ... | @@ -169,6 +171,7 @@ pub const Inst = struct { | ... | @@ -169,6 +171,7 @@ pub const Inst = struct { |
| 169 | .primitive, | 171 | .primitive, |
| 170 | .intcast, | 172 | .intcast, |
| 171 | .bitcast, | 173 | .bitcast, |
| 174 | .floatcast, | ||
| 172 | .elemptr, | 175 | .elemptr, |
| 173 | .add, | 176 | .add, |
| 174 | .sub, | 177 | .sub, |
| ... | @@ -556,19 +559,33 @@ pub const Inst = struct { | ... | @@ -556,19 +559,33 @@ pub const Inst = struct { |
| 556 | }; | 559 | }; |
| 557 | }; | 560 | }; |
| 558 | 561 | ||
| 562 | pub const FloatCast = struct { | ||
| 563 | pub const base_tag = Tag.floatcast; | ||
| 564 | pub const builtin_name = "@floatCast"; | ||
| 565 | base: Inst, | ||
| 566 | |||
| 567 | positionals: struct { | ||
| 568 | dest_type: *Inst, | ||
| 569 | operand: *Inst, | ||
| 570 | }, | ||
| 571 | kw_args: struct {}, | ||
| 572 | }; | ||
| 573 | |||
| 559 | pub const IntCast = struct { | 574 | pub const IntCast = struct { |
| 560 | pub const base_tag = Tag.intcast; | 575 | pub const base_tag = Tag.intcast; |
| 576 | pub const builtin_name = "@intCast"; | ||
| 561 | base: Inst, | 577 | base: Inst, |
| 562 | 578 | ||
| 563 | positionals: struct { | 579 | positionals: struct { |
| 564 | dest_type: *Inst, | 580 | dest_type: *Inst, |
| 565 | value: *Inst, | 581 | operand: *Inst, |
| 566 | }, | 582 | }, |
| 567 | kw_args: struct {}, | 583 | kw_args: struct {}, |
| 568 | }; | 584 | }; |
| 569 | 585 | ||
| 570 | pub const BitCast = struct { | 586 | pub const BitCast = struct { |
| 571 | pub const base_tag = Tag.bitcast; | 587 | pub const base_tag = Tag.bitcast; |
| 588 | pub const builtin_name = "@bitCast"; | ||
| 572 | base: Inst, | 589 | base: Inst, |
| 573 | 590 | ||
| 574 | positionals: struct { | 591 | positionals: struct { |
| ... | @@ -1618,6 +1635,28 @@ const EmitZIR = struct { | ... | @@ -1618,6 +1635,28 @@ const EmitZIR = struct { |
| 1618 | return &new_inst.base; | 1635 | return &new_inst.base; |
| 1619 | } | 1636 | } |
| 1620 | 1637 | ||
| 1638 | fn emitCast( | ||
| 1639 | self: *EmitZIR, | ||
| 1640 | src: usize, | ||
| 1641 | new_body: ZirBody, | ||
| 1642 | old_inst: *ir.Inst.UnOp, | ||
| 1643 | comptime I: type, | ||
| 1644 | ) Allocator.Error!*Inst { | ||
| 1645 | const new_inst = try self.arena.allocator.create(I); | ||
| 1646 | new_inst.* = .{ | ||
| 1647 | .base = .{ | ||
| 1648 | .src = src, | ||
| 1649 | .tag = I.base_tag, | ||
| 1650 | }, | ||
| 1651 | .positionals = .{ | ||
| 1652 | .dest_type = (try self.emitType(src, old_inst.base.ty)).inst, | ||
| 1653 | .operand = try self.resolveInst(new_body, old_inst.operand), | ||
| 1654 | }, | ||
| 1655 | .kw_args = .{}, | ||
| 1656 | }; | ||
| 1657 | return &new_inst.base; | ||
| 1658 | } | ||
| 1659 | |||
| 1621 | fn emitBody( | 1660 | fn emitBody( |
| 1622 | self: *EmitZIR, | 1661 | self: *EmitZIR, |
| 1623 | body: ir.Body, | 1662 | body: ir.Body, |
| ... | @@ -1652,22 +1691,9 @@ const EmitZIR = struct { | ... | @@ -1652,22 +1691,9 @@ const EmitZIR = struct { |
| 1652 | .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt), | 1691 | .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt), |
| 1653 | .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq), | 1692 | .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq), |
| 1654 | 1693 | ||
| 1655 | .bitcast => blk: { | 1694 | .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, Inst.BitCast), |
| 1656 | const old_inst = inst.castTag(.bitcast).?; | 1695 | .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, Inst.IntCast), |
| 1657 | const new_inst = try self.arena.allocator.create(Inst.BitCast); | 1696 | .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, Inst.FloatCast), |
| 1658 | new_inst.* = .{ | ||
| 1659 | .base = .{ | ||
| 1660 | .src = inst.src, | ||
| 1661 | .tag = Inst.BitCast.base_tag, | ||
| 1662 | }, | ||
| 1663 | .positionals = .{ | ||
| 1664 | .dest_type = (try self.emitType(inst.src, inst.ty)).inst, | ||
| 1665 | .operand = try self.resolveInst(new_body, old_inst.operand), | ||
| 1666 | }, | ||
| 1667 | .kw_args = .{}, | ||
| 1668 | }; | ||
| 1669 | break :blk &new_inst.base; | ||
| 1670 | }, | ||
| 1671 | 1697 | ||
| 1672 | .block => blk: { | 1698 | .block => blk: { |
| 1673 | const old_inst = inst.castTag(.block).?; | 1699 | const old_inst = inst.castTag(.block).?; |
src/ir.cpp+3-2| ... | @@ -288,6 +288,7 @@ static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* so | ... | @@ -288,6 +288,7 @@ static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* so |
| 288 | static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right); | 288 | static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right); |
| 289 | static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right); | 289 | static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right); |
| 290 | static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field); | 290 | static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field); |
| 291 | static void value_to_bigfloat(BigFloat *out, ZigValue *val); | ||
| 291 | 292 | ||
| 292 | #define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) | 293 | #define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) |
| 293 | #define ir_assert_gen(OK, SOURCE_INSTRUCTION) ir_assert_gen_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) | 294 | #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) { | ... | @@ -10930,8 +10931,8 @@ static Cmp float_cmp(ZigValue *op1, ZigValue *op2) { |
| 10930 | } | 10931 | } |
| 10931 | BigFloat op1_big; | 10932 | BigFloat op1_big; |
| 10932 | BigFloat op2_big; | 10933 | BigFloat op2_big; |
| 10933 | float_init_bigfloat(op1, &op1_big); | 10934 | value_to_bigfloat(&op1_big, op1); |
| 10934 | float_init_bigfloat(op2, &op2_big); | 10935 | value_to_bigfloat(&op2_big, op2); |
| 10935 | return bigfloat_cmp(&op1_big, &op2_big); | 10936 | return bigfloat_cmp(&op1_big, &op2_big); |
| 10936 | } | 10937 | } |
| 10937 | 10938 |
test/stage1/behavior/floatop.zig+11| ... | @@ -434,6 +434,17 @@ fn testFloatComparisons() void { | ... | @@ -434,6 +434,17 @@ fn testFloatComparisons() void { |
| 434 | } | 434 | } |
| 435 | } | 435 | } |
| 436 | 436 | ||
| 437 | test "different sized float comparisons" { | ||
| 438 | testDifferentSizedFloatComparisons(); | ||
| 439 | comptime testDifferentSizedFloatComparisons(); | ||
| 440 | } | ||
| 441 | |||
| 442 | fn testDifferentSizedFloatComparisons() void { | ||
| 443 | var a: f16 = 1; | ||
| 444 | var b: f64 = 2; | ||
| 445 | expect(a < b); | ||
| 446 | } | ||
| 447 | |||
| 437 | // TODO This is waiting on library support for the Windows build (not sure why the other's don't need it) | 448 | // TODO This is waiting on library support for the Windows build (not sure why the other's don't need it) |
| 438 | //test "@nearbyint" { | 449 | //test "@nearbyint" { |
| 439 | // comptime testNearbyInt(); | 450 | // comptime testNearbyInt(); |