| author | |
| committer | |
| log | c4b83ea02102611a85f75b189f0803d9b6a335c2 |
| tree | 5460625c3766085c5f1210f577e3144296c6a986 |
| parent | 5bd464e386df35bfe38b062190074ce3c2689001 |
| signature |
This was also an experiment to see if it were easier to implement a new
feature when using the instruction encoder.
Verdict: It's not that much easier, but I think it's certainly much more
readable, because the description of the Instruction annotates what each
field means. Right now, precise knowledge of x86_64 instructions is
still required because things like when to set the 64-bit flag, how to
read x86_64 instruction references, etc. are still not automatically
done for you.
In the future, this interface might make it sligtly easier to write an
assembler for x86_64, by abstracting the bit-fiddling aspects of
instruction encoding.4 files changed, 318 insertions(+), 0 deletions(-)
src/Module.zig+60| ... | @@ -4330,6 +4330,33 @@ pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { | ... | @@ -4330,6 +4330,33 @@ pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { |
| 4330 | } | 4330 | } |
| 4331 | } | 4331 | } |
| 4332 | 4332 | ||
| 4333 | pub fn intMul(allocator: *Allocator, lhs: Value, rhs: Value) !Value { | ||
| 4334 | // TODO is this a performance issue? maybe we should try the operation without | ||
| 4335 | // resorting to BigInt first. | ||
| 4336 | var lhs_space: Value.BigIntSpace = undefined; | ||
| 4337 | var rhs_space: Value.BigIntSpace = undefined; | ||
| 4338 | const lhs_bigint = lhs.toBigInt(&lhs_space); | ||
| 4339 | const rhs_bigint = rhs.toBigInt(&rhs_space); | ||
| 4340 | const limbs = try allocator.alloc( | ||
| 4341 | std.math.big.Limb, | ||
| 4342 | lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1, | ||
| 4343 | ); | ||
| 4344 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | ||
| 4345 | var limbs_buffer = try allocator.alloc( | ||
| 4346 | std.math.big.Limb, | ||
| 4347 | std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1), | ||
| 4348 | ); | ||
| 4349 | defer allocator.free(limbs_buffer); | ||
| 4350 | result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator); | ||
| 4351 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | ||
| 4352 | |||
| 4353 | if (result_bigint.positive) { | ||
| 4354 | return Value.Tag.int_big_positive.create(allocator, result_limbs); | ||
| 4355 | } else { | ||
| 4356 | return Value.Tag.int_big_negative.create(allocator, result_limbs); | ||
| 4357 | } | ||
| 4358 | } | ||
| 4359 | |||
| 4333 | pub fn floatAdd( | 4360 | pub fn floatAdd( |
| 4334 | arena: *Allocator, | 4361 | arena: *Allocator, |
| 4335 | float_type: Type, | 4362 | float_type: Type, |
| ... | @@ -4396,6 +4423,39 @@ pub fn floatSub( | ... | @@ -4396,6 +4423,39 @@ pub fn floatSub( |
| 4396 | } | 4423 | } |
| 4397 | } | 4424 | } |
| 4398 | 4425 | ||
| 4426 | pub fn floatMul( | ||
| 4427 | arena: *Allocator, | ||
| 4428 | float_type: Type, | ||
| 4429 | src: LazySrcLoc, | ||
| 4430 | lhs: Value, | ||
| 4431 | rhs: Value, | ||
| 4432 | ) !Value { | ||
| 4433 | switch (float_type.tag()) { | ||
| 4434 | .f16 => { | ||
| 4435 | @panic("TODO add __trunctfhf2 to compiler-rt"); | ||
| 4436 | //const lhs_val = lhs.toFloat(f16); | ||
| 4437 | //const rhs_val = rhs.toFloat(f16); | ||
| 4438 | //return Value.Tag.float_16.create(arena, lhs_val * rhs_val); | ||
| 4439 | }, | ||
| 4440 | .f32 => { | ||
| 4441 | const lhs_val = lhs.toFloat(f32); | ||
| 4442 | const rhs_val = rhs.toFloat(f32); | ||
| 4443 | return Value.Tag.float_32.create(arena, lhs_val * rhs_val); | ||
| 4444 | }, | ||
| 4445 | .f64 => { | ||
| 4446 | const lhs_val = lhs.toFloat(f64); | ||
| 4447 | const rhs_val = rhs.toFloat(f64); | ||
| 4448 | return Value.Tag.float_64.create(arena, lhs_val * rhs_val); | ||
| 4449 | }, | ||
| 4450 | .f128, .comptime_float, .c_longdouble => { | ||
| 4451 | const lhs_val = lhs.toFloat(f128); | ||
| 4452 | const rhs_val = rhs.toFloat(f128); | ||
| 4453 | return Value.Tag.float_128.create(arena, lhs_val * rhs_val); | ||
| 4454 | }, | ||
| 4455 | else => unreachable, | ||
| 4456 | } | ||
| 4457 | } | ||
| 4458 | |||
| 4399 | pub fn simplePtrType( | 4459 | pub fn simplePtrType( |
| 4400 | mod: *Module, | 4460 | mod: *Module, |
| 4401 | arena: *Allocator, | 4461 | arena: *Allocator, |
src/Sema.zig+7| ... | @@ -3885,6 +3885,13 @@ fn analyzeArithmetic( | ... | @@ -3885,6 +3885,13 @@ fn analyzeArithmetic( |
| 3885 | try Module.floatSub(sema.arena, scalar_type, src, lhs_val, rhs_val); | 3885 | try Module.floatSub(sema.arena, scalar_type, src, lhs_val, rhs_val); |
| 3886 | break :blk val; | 3886 | break :blk val; |
| 3887 | }, | 3887 | }, |
| 3888 | .mul => blk: { | ||
| 3889 | const val = if (is_int) | ||
| 3890 | try Module.intMul(sema.arena, lhs_val, rhs_val) | ||
| 3891 | else | ||
| 3892 | try Module.floatMul(sema.arena, scalar_type, src, lhs_val, rhs_val); | ||
| 3893 | break :blk val; | ||
| 3894 | }, | ||
| 3888 | else => return sema.mod.fail(&block.base, src, "TODO Implement arithmetic operand '{s}'", .{@tagName(zir_tag)}), | 3895 | else => return sema.mod.fail(&block.base, src, "TODO Implement arithmetic operand '{s}'", .{@tagName(zir_tag)}), |
| 3889 | }; | 3896 | }; |
| 3890 | 3897 |
src/codegen.zig+149| ... | @@ -1079,6 +1079,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1079,6 +1079,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1079 | if (inst.base.isUnused()) | 1079 | if (inst.base.isUnused()) |
| 1080 | return MCValue.dead; | 1080 | return MCValue.dead; |
| 1081 | switch (arch) { | 1081 | switch (arch) { |
| 1082 | .x86_64 => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs), | ||
| 1082 | .arm, .armeb => return try self.genArmMul(&inst.base, inst.lhs, inst.rhs), | 1083 | .arm, .armeb => return try self.genArmMul(&inst.base, inst.lhs, inst.rhs), |
| 1083 | else => return self.fail(inst.base.src, "TODO implement mul for {}", .{self.target.cpu.arch}), | 1084 | else => return self.fail(inst.base.src, "TODO implement mul for {}", .{self.target.cpu.arch}), |
| 1084 | } | 1085 | } |
| ... | @@ -1574,6 +1575,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1574,6 +1575,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1574 | .sub, .subwrap => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 5, 0x28), | 1575 | .sub, .subwrap => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 5, 0x28), |
| 1575 | .xor, .not => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 6, 0x30), | 1576 | .xor, .not => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 6, 0x30), |
| 1576 | 1577 | ||
| 1578 | .mul, .mulwrap => try self.genX8664Imul(inst.src, inst.ty, dst_mcv, src_mcv), | ||
| 1577 | else => unreachable, | 1579 | else => unreachable, |
| 1578 | } | 1580 | } |
| 1579 | 1581 | ||
| ... | @@ -1795,6 +1797,153 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1795,6 +1797,153 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1795 | } | 1797 | } |
| 1796 | } | 1798 | } |
| 1797 | 1799 | ||
| 1800 | /// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv. | ||
| 1801 | fn genX8664Imul( | ||
| 1802 | self: *Self, | ||
| 1803 | src: LazySrcLoc, | ||
| 1804 | dst_ty: Type, | ||
| 1805 | dst_mcv: MCValue, | ||
| 1806 | src_mcv: MCValue, | ||
| 1807 | ) !void { | ||
| 1808 | switch (dst_mcv) { | ||
| 1809 | .none => unreachable, | ||
| 1810 | .undef => unreachable, | ||
| 1811 | .dead, .unreach, .immediate => unreachable, | ||
| 1812 | .compare_flags_unsigned => unreachable, | ||
| 1813 | .compare_flags_signed => unreachable, | ||
| 1814 | .ptr_stack_offset => unreachable, | ||
| 1815 | .ptr_embedded_in_code => unreachable, | ||
| 1816 | .register => |dst_reg| { | ||
| 1817 | switch (src_mcv) { | ||
| 1818 | .none => unreachable, | ||
| 1819 | .undef => try self.genSetReg(src, dst_ty, dst_reg, .undef), | ||
| 1820 | .dead, .unreach => unreachable, | ||
| 1821 | .ptr_stack_offset => unreachable, | ||
| 1822 | .ptr_embedded_in_code => unreachable, | ||
| 1823 | .register => |src_reg| { | ||
| 1824 | // register, register | ||
| 1825 | // | ||
| 1826 | // Use the following imul opcode | ||
| 1827 | // 0F AF /r: IMUL r32/64, r/m32/64 | ||
| 1828 | try self.encodeX8664Instruction(src, Instruction{ | ||
| 1829 | .operand_size_64 = dst_ty.abiSize(self.target.*) == 64, | ||
| 1830 | .primary_opcode_2b = 0xaf, | ||
| 1831 | // TODO: Explicit optional wrap due to stage 1 miscompilation :( | ||
| 1832 | // https://github.com/ziglang/zig/issues/6515 | ||
| 1833 | .modrm = @as( | ||
| 1834 | ?Instruction.ModrmEffectiveAddress, | ||
| 1835 | Instruction.ModrmEffectiveAddress{ .reg = src_reg }, | ||
| 1836 | ), | ||
| 1837 | .reg = dst_reg, | ||
| 1838 | }); | ||
| 1839 | }, | ||
| 1840 | .immediate => |imm| { | ||
| 1841 | // register, immediate: | ||
| 1842 | // depends on size of immediate. | ||
| 1843 | // | ||
| 1844 | // immediate fits in i8: | ||
| 1845 | // 6B /r ib: IMUL r32/64, r/m32/64, imm8 | ||
| 1846 | // | ||
| 1847 | // immediate fits in i32: | ||
| 1848 | // 69 /r id: IMUL r32/64, r/m32/64, imm32 | ||
| 1849 | // | ||
| 1850 | // immediate is huge: | ||
| 1851 | // split into 2 instructions | ||
| 1852 | // 1) copy the 64 bit immediate into a tmp register | ||
| 1853 | // 2) perform register,register mul | ||
| 1854 | // 0F AF /r: IMUL r32/64, r/m32/64 | ||
| 1855 | if (math.minInt(i8) <= imm and imm <= math.maxInt(i8)) { | ||
| 1856 | try self.encodeX8664Instruction(src, Instruction{ | ||
| 1857 | .operand_size_64 = dst_ty.abiSize(self.target.*) == 64, | ||
| 1858 | .primary_opcode_1b = 0x6B, | ||
| 1859 | .reg = dst_reg, | ||
| 1860 | // TODO: Explicit optional wrap due to stage 1 miscompilation :( | ||
| 1861 | // https://github.com/ziglang/zig/issues/6515 | ||
| 1862 | .modrm = @as( | ||
| 1863 | ?Instruction.ModrmEffectiveAddress, | ||
| 1864 | Instruction.ModrmEffectiveAddress{ .reg = dst_reg }, | ||
| 1865 | ), | ||
| 1866 | .immediate_bytes = 1, | ||
| 1867 | .immediate = imm, | ||
| 1868 | }); | ||
| 1869 | } else if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) { | ||
| 1870 | try self.encodeX8664Instruction(src, Instruction{ | ||
| 1871 | .operand_size_64 = dst_ty.abiSize(self.target.*) == 64, | ||
| 1872 | .primary_opcode_1b = 0x69, | ||
| 1873 | .reg = dst_reg, | ||
| 1874 | // TODO: Explicit optional wrap due to stage 1 miscompilation :( | ||
| 1875 | // https://github.com/ziglang/zig/issues/6515 | ||
| 1876 | .modrm = @as( | ||
| 1877 | ?Instruction.ModrmEffectiveAddress, | ||
| 1878 | Instruction.ModrmEffectiveAddress{ .reg = dst_reg }, | ||
| 1879 | ), | ||
| 1880 | .immediate_bytes = 4, | ||
| 1881 | .immediate = imm, | ||
| 1882 | }); | ||
| 1883 | } else { | ||
| 1884 | const src_reg = try self.copyToTmpRegister(src, dst_ty, src_mcv); | ||
| 1885 | return self.genX8664Imul(src, dst_ty, dst_mcv, MCValue{ .register = src_reg }); | ||
| 1886 | } | ||
| 1887 | }, | ||
| 1888 | .embedded_in_code, .memory, .stack_offset => { | ||
| 1889 | return self.fail(src, "TODO implement x86 multiply source memory", .{}); | ||
| 1890 | }, | ||
| 1891 | .compare_flags_unsigned => { | ||
| 1892 | return self.fail(src, "TODO implement x86 multiply source compare flag (unsigned)", .{}); | ||
| 1893 | }, | ||
| 1894 | .compare_flags_signed => { | ||
| 1895 | return self.fail(src, "TODO implement x86 multiply source compare flag (signed)", .{}); | ||
| 1896 | }, | ||
| 1897 | } | ||
| 1898 | }, | ||
| 1899 | .stack_offset => |off| { | ||
| 1900 | switch (src_mcv) { | ||
| 1901 | .none => unreachable, | ||
| 1902 | .undef => return self.genSetStack(src, dst_ty, off, .undef), | ||
| 1903 | .dead, .unreach => unreachable, | ||
| 1904 | .ptr_stack_offset => unreachable, | ||
| 1905 | .ptr_embedded_in_code => unreachable, | ||
| 1906 | .register => |src_reg| { | ||
| 1907 | // copy dst to a register | ||
| 1908 | const dst_reg = try self.copyToTmpRegister(src, dst_ty, dst_mcv); | ||
| 1909 | // multiply into dst_reg | ||
| 1910 | // register, register | ||
| 1911 | // Use the following imul opcode | ||
| 1912 | // 0F AF /r: IMUL r32/64, r/m32/64 | ||
| 1913 | try self.encodeX8664Instruction(src, Instruction{ | ||
| 1914 | .operand_size_64 = dst_ty.abiSize(self.target.*) == 64, | ||
| 1915 | .primary_opcode_2b = 0xaf, | ||
| 1916 | // TODO: Explicit optional wrap due to stage 1 miscompilation :( | ||
| 1917 | // https://github.com/ziglang/zig/issues/6515 | ||
| 1918 | .modrm = @as( | ||
| 1919 | ?Instruction.ModrmEffectiveAddress, | ||
| 1920 | Instruction.ModrmEffectiveAddress{ .reg = src_reg }, | ||
| 1921 | ), | ||
| 1922 | .reg = dst_reg, | ||
| 1923 | }); | ||
| 1924 | // copy dst_reg back out | ||
| 1925 | return self.genSetStack(src, dst_ty, off, MCValue{ .register = dst_reg }); | ||
| 1926 | }, | ||
| 1927 | .immediate => |imm| { | ||
| 1928 | return self.fail(src, "TODO implement x86 multiply source immediate", .{}); | ||
| 1929 | }, | ||
| 1930 | .embedded_in_code, .memory, .stack_offset => { | ||
| 1931 | return self.fail(src, "TODO implement x86 multiply source memory", .{}); | ||
| 1932 | }, | ||
| 1933 | .compare_flags_unsigned => { | ||
| 1934 | return self.fail(src, "TODO implement x86 multiply source compare flag (unsigned)", .{}); | ||
| 1935 | }, | ||
| 1936 | .compare_flags_signed => { | ||
| 1937 | return self.fail(src, "TODO implement x86 multiply source compare flag (signed)", .{}); | ||
| 1938 | }, | ||
| 1939 | } | ||
| 1940 | }, | ||
| 1941 | .embedded_in_code, .memory => { | ||
| 1942 | return self.fail(src, "TODO implement x86 multiply destination memory", .{}); | ||
| 1943 | }, | ||
| 1944 | } | ||
| 1945 | } | ||
| 1946 | |||
| 1798 | fn genX8664ModRMRegToStack(self: *Self, src: LazySrcLoc, ty: Type, off: u32, reg: Register, opcode: u8) !void { | 1947 | fn genX8664ModRMRegToStack(self: *Self, src: LazySrcLoc, ty: Type, off: u32, reg: Register, opcode: u8) !void { |
| 1799 | const abi_size = ty.abiSize(self.target.*); | 1948 | const abi_size = ty.abiSize(self.target.*); |
| 1800 | const adj_off = off + abi_size; | 1949 | const adj_off = off + abi_size; |
test/stage2/test.zig+102| ... | @@ -358,6 +358,81 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -358,6 +358,81 @@ pub fn addCases(ctx: *TestContext) !void { |
| 358 | , &[_][]const u8{":2:15: error: incompatible types: 'bool' and 'comptime_int'"}); | 358 | , &[_][]const u8{":2:15: error: incompatible types: 'bool' and 'comptime_int'"}); |
| 359 | } | 359 | } |
| 360 | 360 | ||
| 361 | { | ||
| 362 | var case = ctx.exe("multiplying numbers at runtime and comptime", linux_x64); | ||
| 363 | case.addCompareOutput( | ||
| 364 | \\export fn _start() noreturn { | ||
| 365 | \\ mul(3, 4); | ||
| 366 | \\ | ||
| 367 | \\ exit(); | ||
| 368 | \\} | ||
| 369 | \\ | ||
| 370 | \\fn mul(a: u32, b: u32) void { | ||
| 371 | \\ if (a * b != 12) unreachable; | ||
| 372 | \\} | ||
| 373 | \\ | ||
| 374 | \\fn exit() noreturn { | ||
| 375 | \\ asm volatile ("syscall" | ||
| 376 | \\ : | ||
| 377 | \\ : [number] "{rax}" (231), | ||
| 378 | \\ [arg1] "{rdi}" (0) | ||
| 379 | \\ : "rcx", "r11", "memory" | ||
| 380 | \\ ); | ||
| 381 | \\ unreachable; | ||
| 382 | \\} | ||
| 383 | , | ||
| 384 | "", | ||
| 385 | ); | ||
| 386 | // comptime function call | ||
| 387 | case.addCompareOutput( | ||
| 388 | \\export fn _start() noreturn { | ||
| 389 | \\ exit(); | ||
| 390 | \\} | ||
| 391 | \\ | ||
| 392 | \\fn mul(a: u32, b: u32) u32 { | ||
| 393 | \\ return a * b; | ||
| 394 | \\} | ||
| 395 | \\ | ||
| 396 | \\const x = mul(3, 4); | ||
| 397 | \\ | ||
| 398 | \\fn exit() noreturn { | ||
| 399 | \\ asm volatile ("syscall" | ||
| 400 | \\ : | ||
| 401 | \\ : [number] "{rax}" (231), | ||
| 402 | \\ [arg1] "{rdi}" (x - 12) | ||
| 403 | \\ : "rcx", "r11", "memory" | ||
| 404 | \\ ); | ||
| 405 | \\ unreachable; | ||
| 406 | \\} | ||
| 407 | , | ||
| 408 | "", | ||
| 409 | ); | ||
| 410 | // Inline function call | ||
| 411 | case.addCompareOutput( | ||
| 412 | \\export fn _start() noreturn { | ||
| 413 | \\ var x: usize = 5; | ||
| 414 | \\ const y = mul(2, 3, x); | ||
| 415 | \\ exit(y - 30); | ||
| 416 | \\} | ||
| 417 | \\ | ||
| 418 | \\fn mul(a: usize, b: usize, c: usize) callconv(.Inline) usize { | ||
| 419 | \\ return a * b * c; | ||
| 420 | \\} | ||
| 421 | \\ | ||
| 422 | \\fn exit(code: usize) noreturn { | ||
| 423 | \\ asm volatile ("syscall" | ||
| 424 | \\ : | ||
| 425 | \\ : [number] "{rax}" (231), | ||
| 426 | \\ [arg1] "{rdi}" (code) | ||
| 427 | \\ : "rcx", "r11", "memory" | ||
| 428 | \\ ); | ||
| 429 | \\ unreachable; | ||
| 430 | \\} | ||
| 431 | , | ||
| 432 | "", | ||
| 433 | ); | ||
| 434 | } | ||
| 435 | |||
| 361 | { | 436 | { |
| 362 | var case = ctx.exe("assert function", linux_x64); | 437 | var case = ctx.exe("assert function", linux_x64); |
| 363 | case.addCompareOutput( | 438 | case.addCompareOutput( |
| ... | @@ -741,6 +816,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -741,6 +816,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 741 | case.addCompareOutput( | 816 | case.addCompareOutput( |
| 742 | \\export fn _start() noreturn { | 817 | \\export fn _start() noreturn { |
| 743 | \\ assert(add(3, 4) == 1221); | 818 | \\ assert(add(3, 4) == 1221); |
| 819 | \\ assert(mul(3, 4) == 21609); | ||
| 744 | \\ | 820 | \\ |
| 745 | \\ exit(); | 821 | \\ exit(); |
| 746 | \\} | 822 | \\} |
| ... | @@ -774,6 +850,32 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -774,6 +850,32 @@ pub fn addCases(ctx: *TestContext) !void { |
| 774 | \\ return z; | 850 | \\ return z; |
| 775 | \\} | 851 | \\} |
| 776 | \\ | 852 | \\ |
| 853 | \\fn mul(a: u32, b: u32) u32 { | ||
| 854 | \\ const x: u32 = blk: { | ||
| 855 | \\ const c = a * a * a * a; // 81 | ||
| 856 | \\ const d = a * a * a * b; // 108 | ||
| 857 | \\ const e = a * a * b * a; // 108 | ||
| 858 | \\ const f = a * a * b * b; // 144 | ||
| 859 | \\ const g = a * b * a * a; // 108 | ||
| 860 | \\ const h = a * b * a * b; // 144 | ||
| 861 | \\ const i = a * b * b * a; // 144 | ||
| 862 | \\ const j = a * b * b * b; // 192 | ||
| 863 | \\ const k = b * a * a * a; // 108 | ||
| 864 | \\ const l = b * a * a * b; // 144 | ||
| 865 | \\ const m = b * a * b * a; // 144 | ||
| 866 | \\ const n = b * a * b * b; // 192 | ||
| 867 | \\ const o = b * b * a * a; // 144 | ||
| 868 | \\ const p = b * b * a * b; // 192 | ||
| 869 | \\ const q = b * b * b * a; // 192 | ||
| 870 | \\ const r = b * b * b * b; // 256 | ||
| 871 | \\ const s = c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r; // 2401 | ||
| 872 | \\ break :blk s; | ||
| 873 | \\ }; | ||
| 874 | \\ const y = x * a; // 7203 | ||
| 875 | \\ const z = y * a; // 21609 | ||
| 876 | \\ return z; | ||
| 877 | \\} | ||
| 878 | \\ | ||
| 777 | \\pub fn assert(ok: bool) void { | 879 | \\pub fn assert(ok: bool) void { |
| 778 | \\ if (!ok) unreachable; // assertion failure | 880 | \\ if (!ok) unreachable; // assertion failure |
| 779 | \\} | 881 | \\} |