Merge pull request #5192 from ziglang/stage2-tests
add ZIR compare output test case to test suitemaster
commit
3386bb896d
|
@ -44,7 +44,7 @@ pub fn build(b: *Builder) !void {
|
|||
try findAndReadConfigH(b);
|
||||
|
||||
var test_stage2 = b.addTest("src-self-hosted/test.zig");
|
||||
test_stage2.setBuildMode(builtin.Mode.Debug);
|
||||
test_stage2.setBuildMode(.Debug); // note this is only the mode of the test harness
|
||||
test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig");
|
||||
|
||||
const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
|
||||
|
@ -68,7 +68,6 @@ pub fn build(b: *Builder) !void {
|
|||
var ctx = parseConfigH(b, config_h_text);
|
||||
ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
|
||||
|
||||
try configureStage2(b, test_stage2, ctx);
|
||||
try configureStage2(b, exe, ctx);
|
||||
|
||||
b.default_step.dependOn(&exe.step);
|
||||
|
|
|
@ -46,6 +46,12 @@ pub const ChildProcess = struct {
|
|||
|
||||
/// Set to change the current working directory when spawning the child process.
|
||||
cwd: ?[]const u8,
|
||||
/// Set to change the current working directory when spawning the child process.
|
||||
/// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190
|
||||
/// Once that is done, `cwd` will be deprecated in favor of this field.
|
||||
/// The directory handle must be opened with the ability to be passed
|
||||
/// to a child process (no `O_CLOEXEC` flag on POSIX).
|
||||
cwd_dir: ?fs.Dir = null,
|
||||
|
||||
err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t,
|
||||
|
||||
|
@ -183,6 +189,7 @@ pub const ChildProcess = struct {
|
|||
allocator: *mem.Allocator,
|
||||
argv: []const []const u8,
|
||||
cwd: ?[]const u8 = null,
|
||||
cwd_dir: ?fs.Dir = null,
|
||||
env_map: ?*const BufMap = null,
|
||||
max_output_bytes: usize = 50 * 1024,
|
||||
expand_arg0: Arg0Expand = .no_expand,
|
||||
|
@ -194,6 +201,7 @@ pub const ChildProcess = struct {
|
|||
child.stdout_behavior = .Pipe;
|
||||
child.stderr_behavior = .Pipe;
|
||||
child.cwd = args.cwd;
|
||||
child.cwd_dir = args.cwd_dir;
|
||||
child.env_map = args.env_map;
|
||||
child.expand_arg0 = args.expand_arg0;
|
||||
|
||||
|
@ -414,7 +422,9 @@ pub const ChildProcess = struct {
|
|||
os.close(stderr_pipe[1]);
|
||||
}
|
||||
|
||||
if (self.cwd) |cwd| {
|
||||
if (self.cwd_dir) |cwd| {
|
||||
os.fchdir(cwd.fd) catch |err| forkChildErrReport(err_pipe[1], err);
|
||||
} else if (self.cwd) |cwd| {
|
||||
os.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);
|
||||
}
|
||||
|
||||
|
|
|
@ -1058,7 +1058,7 @@ pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
|
|||
return value;
|
||||
}
|
||||
|
||||
fn digitToChar(digit: u8, uppercase: bool) u8 {
|
||||
pub fn digitToChar(digit: u8, uppercase: bool) u8 {
|
||||
return switch (digit) {
|
||||
0...9 => digit + '0',
|
||||
10...35 => digit + ((if (uppercase) @as(u8, 'A') else @as(u8, 'a')) - 10),
|
||||
|
|
|
@ -606,7 +606,8 @@ pub const Dir = struct {
|
|||
} else 0;
|
||||
|
||||
const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
|
||||
const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read)
|
||||
const O_CLOEXEC: u32 = if (flags.share_with_child_process) 0 else os.O_CLOEXEC;
|
||||
const os_flags = lock_flag | O_LARGEFILE | O_CLOEXEC | if (flags.write and flags.read)
|
||||
@as(u32, os.O_RDWR)
|
||||
else if (flags.write)
|
||||
@as(u32, os.O_WRONLY)
|
||||
|
@ -689,7 +690,8 @@ pub const Dir = struct {
|
|||
} else 0;
|
||||
|
||||
const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
|
||||
const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC |
|
||||
const O_CLOEXEC: u32 = if (flags.share_with_child_process) 0 else os.O_CLOEXEC;
|
||||
const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | O_CLOEXEC |
|
||||
(if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |
|
||||
(if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |
|
||||
(if (flags.exclusive) @as(u32, os.O_EXCL) else 0);
|
||||
|
@ -787,6 +789,15 @@ pub const Dir = struct {
|
|||
}
|
||||
}
|
||||
|
||||
/// This function performs `makePath`, followed by `openDir`.
|
||||
/// If supported by the OS, this operation is atomic. It is not atomic on
|
||||
/// all operating systems.
|
||||
pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !Dir {
|
||||
// TODO improve this implementation on Windows; we can avoid 1 call to NtClose
|
||||
try self.makePath(sub_path);
|
||||
return self.openDir(sub_path, open_dir_options);
|
||||
}
|
||||
|
||||
/// Changes the current working directory to the open directory handle.
|
||||
/// This modifies global state and can have surprising effects in multi-
|
||||
/// threaded applications. Most applications and especially libraries should
|
||||
|
@ -807,6 +818,11 @@ pub const Dir = struct {
|
|||
/// `true` means the opened directory can be scanned for the files and sub-directories
|
||||
/// of the result. It means the `iterate` function can be called.
|
||||
iterate: bool = false,
|
||||
|
||||
/// `true` means the opened directory can be passed to a child process.
|
||||
/// `false` means the directory handle is considered to be closed when a child
|
||||
/// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX.
|
||||
share_with_child_process: bool = false,
|
||||
};
|
||||
|
||||
/// Opens a directory at the given path. The directory is a system resource that remains
|
||||
|
@ -832,9 +848,11 @@ pub const Dir = struct {
|
|||
return self.openDirW(&sub_path_w, args);
|
||||
} else if (!args.iterate) {
|
||||
const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0;
|
||||
return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH);
|
||||
const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC;
|
||||
return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC | O_PATH);
|
||||
} else {
|
||||
return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC);
|
||||
const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC;
|
||||
return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,11 @@ pub const File = struct {
|
|||
/// It allows the use of `noasync` when calling functions related to opening
|
||||
/// the file, reading, and writing.
|
||||
always_blocking: bool = false,
|
||||
|
||||
/// `true` means the opened directory can be passed to a child process.
|
||||
/// `false` means the directory handle is considered to be closed when a child
|
||||
/// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX.
|
||||
share_with_child_process: bool = false,
|
||||
};
|
||||
|
||||
/// TODO https://github.com/ziglang/zig/issues/3802
|
||||
|
@ -107,6 +112,11 @@ pub const File = struct {
|
|||
/// For POSIX systems this is the file system mode the file will
|
||||
/// be created with.
|
||||
mode: Mode = default_mode,
|
||||
|
||||
/// `true` means the opened directory can be passed to a child process.
|
||||
/// `false` means the directory handle is considered to be closed when a child
|
||||
/// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX.
|
||||
share_with_child_process: bool = false,
|
||||
};
|
||||
|
||||
/// Upon success, the stream is in an uninitialized state. To continue using it,
|
||||
|
|
|
@ -986,6 +986,43 @@ pub const Order = enum {
|
|||
|
||||
/// Greater than (`>`)
|
||||
gt,
|
||||
|
||||
pub fn invert(self: Order) Order {
|
||||
return switch (self) {
|
||||
.lt => .gt,
|
||||
.eq => .eq,
|
||||
.gt => .gt,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn compare(self: Order, op: CompareOperator) bool {
|
||||
return switch (self) {
|
||||
.lt => switch (op) {
|
||||
.lt => true,
|
||||
.lte => true,
|
||||
.eq => false,
|
||||
.gte => false,
|
||||
.gt => false,
|
||||
.neq => true,
|
||||
},
|
||||
.eq => switch (op) {
|
||||
.lt => false,
|
||||
.lte => true,
|
||||
.eq => true,
|
||||
.gte => true,
|
||||
.gt => false,
|
||||
.neq => false,
|
||||
},
|
||||
.gt => switch (op) {
|
||||
.lt => false,
|
||||
.lte => false,
|
||||
.eq => false,
|
||||
.gte => true,
|
||||
.gt => true,
|
||||
.neq => true,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Given two numbers, this function returns the order they are with respect to each other.
|
||||
|
|
|
@ -1,7 +1,24 @@
|
|||
pub usingnamespace @import("big/int.zig");
|
||||
pub usingnamespace @import("big/rational.zig");
|
||||
const std = @import("../std.zig");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
test "math.big" {
|
||||
_ = @import("big/int.zig");
|
||||
_ = @import("big/rational.zig");
|
||||
pub const Rational = @import("big/rational.zig").Rational;
|
||||
pub const int = @import("big/int.zig");
|
||||
pub const Limb = usize;
|
||||
pub const DoubleLimb = std.meta.IntType(false, 2 * Limb.bit_count);
|
||||
pub const SignedDoubleLimb = std.meta.IntType(true, DoubleLimb.bit_count);
|
||||
pub const Log2Limb = std.math.Log2Int(Limb);
|
||||
|
||||
comptime {
|
||||
assert(std.math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count);
|
||||
assert(Limb.bit_count <= 64); // u128 set is unsupported
|
||||
assert(Limb.is_signed == false);
|
||||
}
|
||||
|
||||
test "" {
|
||||
_ = int;
|
||||
_ = Rational;
|
||||
_ = Limb;
|
||||
_ = DoubleLimb;
|
||||
_ = SignedDoubleLimb;
|
||||
_ = Log2Limb;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -5,10 +5,10 @@ const mem = std.mem;
|
|||
const testing = std.testing;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
const bn = @import("int.zig");
|
||||
const Limb = bn.Limb;
|
||||
const DoubleLimb = bn.DoubleLimb;
|
||||
const Int = bn.Int;
|
||||
const Limb = std.math.big.Limb;
|
||||
const DoubleLimb = std.math.big.DoubleLimb;
|
||||
const Int = std.math.big.int.Managed;
|
||||
const IntConst = std.math.big.int.Const;
|
||||
|
||||
/// An arbitrary-precision rational number.
|
||||
///
|
||||
|
@ -17,6 +17,9 @@ const Int = bn.Int;
|
|||
///
|
||||
/// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers,
|
||||
/// gcd(p, q) = 1 always.
|
||||
///
|
||||
/// TODO rework this to store its own allocator and use a non-managed big int, to avoid double
|
||||
/// allocator storage.
|
||||
pub const Rational = struct {
|
||||
/// Numerator. Determines the sign of the Rational.
|
||||
p: Int,
|
||||
|
@ -98,20 +101,20 @@ pub const Rational = struct {
|
|||
if (point) |i| {
|
||||
try self.p.setString(10, str[0..i]);
|
||||
|
||||
const base = Int.initFixed(([_]Limb{10})[0..]);
|
||||
const base = IntConst{ .limbs = &[_]Limb{10}, .positive = true };
|
||||
|
||||
var j: usize = start;
|
||||
while (j < str.len - i - 1) : (j += 1) {
|
||||
try self.p.mul(self.p, base);
|
||||
try self.p.mul(self.p.toConst(), base);
|
||||
}
|
||||
|
||||
try self.q.setString(10, str[i + 1 ..]);
|
||||
try self.p.add(self.p, self.q);
|
||||
try self.p.add(self.p.toConst(), self.q.toConst());
|
||||
|
||||
try self.q.set(1);
|
||||
var k: usize = i + 1;
|
||||
while (k < str.len) : (k += 1) {
|
||||
try self.q.mul(self.q, base);
|
||||
try self.q.mul(self.q.toConst(), base);
|
||||
}
|
||||
|
||||
try self.reduce();
|
||||
|
@ -218,14 +221,14 @@ pub const Rational = struct {
|
|||
}
|
||||
|
||||
// 2. compute quotient and remainder
|
||||
var q = try Int.init(self.p.allocator.?);
|
||||
var q = try Int.init(self.p.allocator);
|
||||
defer q.deinit();
|
||||
|
||||
// unused
|
||||
var r = try Int.init(self.p.allocator.?);
|
||||
var r = try Int.init(self.p.allocator);
|
||||
defer r.deinit();
|
||||
|
||||
try Int.divTrunc(&q, &r, a2, b2);
|
||||
try Int.divTrunc(&q, &r, a2.toConst(), b2.toConst());
|
||||
|
||||
var mantissa = extractLowBits(q, BitReprType);
|
||||
var have_rem = r.len() > 0;
|
||||
|
@ -293,14 +296,14 @@ pub const Rational = struct {
|
|||
|
||||
/// Set a Rational directly from an Int.
|
||||
pub fn copyInt(self: *Rational, a: Int) !void {
|
||||
try self.p.copy(a);
|
||||
try self.p.copy(a.toConst());
|
||||
try self.q.set(1);
|
||||
}
|
||||
|
||||
/// Set a Rational directly from a ratio of two Int's.
|
||||
pub fn copyRatio(self: *Rational, a: Int, b: Int) !void {
|
||||
try self.p.copy(a);
|
||||
try self.q.copy(b);
|
||||
try self.p.copy(a.toConst());
|
||||
try self.q.copy(b.toConst());
|
||||
|
||||
self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);
|
||||
self.q.setSign(true);
|
||||
|
@ -327,13 +330,13 @@ pub const Rational = struct {
|
|||
|
||||
/// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a
|
||||
/// > b respectively.
|
||||
pub fn cmp(a: Rational, b: Rational) !math.Order {
|
||||
pub fn order(a: Rational, b: Rational) !math.Order {
|
||||
return cmpInternal(a, b, true);
|
||||
}
|
||||
|
||||
/// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
|
||||
/// |b| or |a| > |b| respectively.
|
||||
pub fn cmpAbs(a: Rational, b: Rational) !math.Order {
|
||||
pub fn orderAbs(a: Rational, b: Rational) !math.Order {
|
||||
return cmpInternal(a, b, false);
|
||||
}
|
||||
|
||||
|
@ -341,16 +344,16 @@ pub const Rational = struct {
|
|||
fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !math.Order {
|
||||
// TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid
|
||||
// the memory allocations here?
|
||||
var q = try Int.init(a.p.allocator.?);
|
||||
var q = try Int.init(a.p.allocator);
|
||||
defer q.deinit();
|
||||
|
||||
var p = try Int.init(b.p.allocator.?);
|
||||
var p = try Int.init(b.p.allocator);
|
||||
defer p.deinit();
|
||||
|
||||
try q.mul(a.p, b.q);
|
||||
try p.mul(b.p, a.q);
|
||||
try q.mul(a.p.toConst(), b.q.toConst());
|
||||
try p.mul(b.p.toConst(), a.q.toConst());
|
||||
|
||||
return if (is_abs) q.cmpAbs(p) else q.cmp(p);
|
||||
return if (is_abs) q.orderAbs(p) else q.order(p);
|
||||
}
|
||||
|
||||
/// rma = a + b.
|
||||
|
@ -364,7 +367,7 @@ pub const Rational = struct {
|
|||
|
||||
var sr: Rational = undefined;
|
||||
if (aliased) {
|
||||
sr = try Rational.init(rma.p.allocator.?);
|
||||
sr = try Rational.init(rma.p.allocator);
|
||||
r = &sr;
|
||||
aliased = true;
|
||||
}
|
||||
|
@ -373,11 +376,11 @@ pub const Rational = struct {
|
|||
r.deinit();
|
||||
};
|
||||
|
||||
try r.p.mul(a.p, b.q);
|
||||
try r.q.mul(b.p, a.q);
|
||||
try r.p.add(r.p, r.q);
|
||||
try r.p.mul(a.p.toConst(), b.q.toConst());
|
||||
try r.q.mul(b.p.toConst(), a.q.toConst());
|
||||
try r.p.add(r.p.toConst(), r.q.toConst());
|
||||
|
||||
try r.q.mul(a.q, b.q);
|
||||
try r.q.mul(a.q.toConst(), b.q.toConst());
|
||||
try r.reduce();
|
||||
}
|
||||
|
||||
|
@ -392,7 +395,7 @@ pub const Rational = struct {
|
|||
|
||||
var sr: Rational = undefined;
|
||||
if (aliased) {
|
||||
sr = try Rational.init(rma.p.allocator.?);
|
||||
sr = try Rational.init(rma.p.allocator);
|
||||
r = &sr;
|
||||
aliased = true;
|
||||
}
|
||||
|
@ -401,11 +404,11 @@ pub const Rational = struct {
|
|||
r.deinit();
|
||||
};
|
||||
|
||||
try r.p.mul(a.p, b.q);
|
||||
try r.q.mul(b.p, a.q);
|
||||
try r.p.sub(r.p, r.q);
|
||||
try r.p.mul(a.p.toConst(), b.q.toConst());
|
||||
try r.q.mul(b.p.toConst(), a.q.toConst());
|
||||
try r.p.sub(r.p.toConst(), r.q.toConst());
|
||||
|
||||
try r.q.mul(a.q, b.q);
|
||||
try r.q.mul(a.q.toConst(), b.q.toConst());
|
||||
try r.reduce();
|
||||
}
|
||||
|
||||
|
@ -415,8 +418,8 @@ pub const Rational = struct {
|
|||
///
|
||||
/// Returns an error if memory could not be allocated.
|
||||
pub fn mul(r: *Rational, a: Rational, b: Rational) !void {
|
||||
try r.p.mul(a.p, b.p);
|
||||
try r.q.mul(a.q, b.q);
|
||||
try r.p.mul(a.p.toConst(), b.p.toConst());
|
||||
try r.q.mul(a.q.toConst(), b.q.toConst());
|
||||
try r.reduce();
|
||||
}
|
||||
|
||||
|
@ -430,8 +433,8 @@ pub const Rational = struct {
|
|||
@panic("division by zero");
|
||||
}
|
||||
|
||||
try r.p.mul(a.p, b.q);
|
||||
try r.q.mul(b.p, a.q);
|
||||
try r.p.mul(a.p.toConst(), b.q.toConst());
|
||||
try r.q.mul(b.p.toConst(), a.q.toConst());
|
||||
try r.reduce();
|
||||
}
|
||||
|
||||
|
@ -442,7 +445,7 @@ pub const Rational = struct {
|
|||
|
||||
// reduce r/q such that gcd(r, q) = 1
|
||||
fn reduce(r: *Rational) !void {
|
||||
var a = try Int.init(r.p.allocator.?);
|
||||
var a = try Int.init(r.p.allocator);
|
||||
defer a.deinit();
|
||||
|
||||
const sign = r.p.isPositive();
|
||||
|
@ -450,15 +453,15 @@ pub const Rational = struct {
|
|||
try a.gcd(r.p, r.q);
|
||||
r.p.setSign(sign);
|
||||
|
||||
const one = Int.initFixed(([_]Limb{1})[0..]);
|
||||
if (a.cmp(one) != .eq) {
|
||||
var unused = try Int.init(r.p.allocator.?);
|
||||
const one = IntConst{ .limbs = &[_]Limb{1}, .positive = true };
|
||||
if (a.toConst().order(one) != .eq) {
|
||||
var unused = try Int.init(r.p.allocator);
|
||||
defer unused.deinit();
|
||||
|
||||
// TODO: divexact would be useful here
|
||||
// TODO: don't copy r.q for div
|
||||
try Int.divTrunc(&r.p, &unused, r.p, a);
|
||||
try Int.divTrunc(&r.q, &unused, r.q, a);
|
||||
try Int.divTrunc(&r.p, &unused, r.p.toConst(), a.toConst());
|
||||
try Int.divTrunc(&r.q, &unused, r.q.toConst(), a.toConst());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -596,25 +599,25 @@ test "big.rational copy" {
|
|||
var a = try Rational.init(testing.allocator);
|
||||
defer a.deinit();
|
||||
|
||||
const b = try Int.initSet(testing.allocator, 5);
|
||||
var b = try Int.initSet(testing.allocator, 5);
|
||||
defer b.deinit();
|
||||
|
||||
try a.copyInt(b);
|
||||
testing.expect((try a.p.to(u32)) == 5);
|
||||
testing.expect((try a.q.to(u32)) == 1);
|
||||
|
||||
const c = try Int.initSet(testing.allocator, 7);
|
||||
var c = try Int.initSet(testing.allocator, 7);
|
||||
defer c.deinit();
|
||||
const d = try Int.initSet(testing.allocator, 3);
|
||||
var d = try Int.initSet(testing.allocator, 3);
|
||||
defer d.deinit();
|
||||
|
||||
try a.copyRatio(c, d);
|
||||
testing.expect((try a.p.to(u32)) == 7);
|
||||
testing.expect((try a.q.to(u32)) == 3);
|
||||
|
||||
const e = try Int.initSet(testing.allocator, 9);
|
||||
var e = try Int.initSet(testing.allocator, 9);
|
||||
defer e.deinit();
|
||||
const f = try Int.initSet(testing.allocator, 3);
|
||||
var f = try Int.initSet(testing.allocator, 3);
|
||||
defer f.deinit();
|
||||
|
||||
try a.copyRatio(e, f);
|
||||
|
@ -680,7 +683,7 @@ test "big.rational swap" {
|
|||
testing.expect((try b.q.to(u32)) == 23);
|
||||
}
|
||||
|
||||
test "big.rational cmp" {
|
||||
test "big.rational order" {
|
||||
var a = try Rational.init(testing.allocator);
|
||||
defer a.deinit();
|
||||
var b = try Rational.init(testing.allocator);
|
||||
|
@ -688,11 +691,11 @@ test "big.rational cmp" {
|
|||
|
||||
try a.setRatio(500, 231);
|
||||
try b.setRatio(18903, 8584);
|
||||
testing.expect((try a.cmp(b)) == .lt);
|
||||
testing.expect((try a.order(b)) == .lt);
|
||||
|
||||
try a.setRatio(890, 10);
|
||||
try b.setRatio(89, 1);
|
||||
testing.expect((try a.cmp(b)) == .eq);
|
||||
testing.expect((try a.order(b)) == .eq);
|
||||
}
|
||||
|
||||
test "big.rational add single-limb" {
|
||||
|
@ -703,11 +706,11 @@ test "big.rational add single-limb" {
|
|||
|
||||
try a.setRatio(500, 231);
|
||||
try b.setRatio(18903, 8584);
|
||||
testing.expect((try a.cmp(b)) == .lt);
|
||||
testing.expect((try a.order(b)) == .lt);
|
||||
|
||||
try a.setRatio(890, 10);
|
||||
try b.setRatio(89, 1);
|
||||
testing.expect((try a.cmp(b)) == .eq);
|
||||
testing.expect((try a.order(b)) == .eq);
|
||||
}
|
||||
|
||||
test "big.rational add" {
|
||||
|
@ -723,7 +726,7 @@ test "big.rational add" {
|
|||
try a.add(a, b);
|
||||
|
||||
try r.setRatio(984786924199, 290395044174);
|
||||
testing.expect((try a.cmp(r)) == .eq);
|
||||
testing.expect((try a.order(r)) == .eq);
|
||||
}
|
||||
|
||||
test "big.rational sub" {
|
||||
|
@ -739,7 +742,7 @@ test "big.rational sub" {
|
|||
try a.sub(a, b);
|
||||
|
||||
try r.setRatio(979040510045, 290395044174);
|
||||
testing.expect((try a.cmp(r)) == .eq);
|
||||
testing.expect((try a.order(r)) == .eq);
|
||||
}
|
||||
|
||||
test "big.rational mul" {
|
||||
|
@ -755,7 +758,7 @@ test "big.rational mul" {
|
|||
try a.mul(a, b);
|
||||
|
||||
try r.setRatio(571481443, 17082061422);
|
||||
testing.expect((try a.cmp(r)) == .eq);
|
||||
testing.expect((try a.order(r)) == .eq);
|
||||
}
|
||||
|
||||
test "big.rational div" {
|
||||
|
@ -771,7 +774,7 @@ test "big.rational div" {
|
|||
try a.div(a, b);
|
||||
|
||||
try r.setRatio(75531824394, 221015929);
|
||||
testing.expect((try a.cmp(r)) == .eq);
|
||||
testing.expect((try a.order(r)) == .eq);
|
||||
}
|
||||
|
||||
test "big.rational div" {
|
||||
|
@ -784,11 +787,11 @@ test "big.rational div" {
|
|||
a.invert();
|
||||
|
||||
try r.setRatio(23341, 78923);
|
||||
testing.expect((try a.cmp(r)) == .eq);
|
||||
testing.expect((try a.order(r)) == .eq);
|
||||
|
||||
try a.setRatio(-78923, 23341);
|
||||
a.invert();
|
||||
|
||||
try r.setRatio(-23341, 78923);
|
||||
testing.expect((try a.cmp(r)) == .eq);
|
||||
testing.expect((try a.order(r)) == .eq);
|
||||
}
|
||||
|
|
|
@ -404,6 +404,7 @@ pub const Target = struct {
|
|||
};
|
||||
|
||||
pub const ObjectFormat = enum {
|
||||
/// TODO Get rid of this one.
|
||||
unknown,
|
||||
coff,
|
||||
elf,
|
||||
|
|
|
@ -12,7 +12,7 @@ pub const failing_allocator = &failing_allocator_instance.allocator;
|
|||
pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0);
|
||||
|
||||
pub var base_allocator_instance = std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]);
|
||||
var allocator_mem: [1024 * 1024]u8 = undefined;
|
||||
var allocator_mem: [2 * 1024 * 1024]u8 = undefined;
|
||||
|
||||
/// This function is intended to be used only in tests. It prints diagnostics to stderr
|
||||
/// and then aborts when actual_error_union is not expected_error.
|
||||
|
@ -193,6 +193,44 @@ pub fn expect(ok: bool) void {
|
|||
if (!ok) @panic("test failure");
|
||||
}
|
||||
|
||||
pub const TmpDir = struct {
|
||||
dir: std.fs.Dir,
|
||||
parent_dir: std.fs.Dir,
|
||||
sub_path: [sub_path_len]u8,
|
||||
|
||||
const random_bytes_count = 12;
|
||||
const sub_path_len = std.base64.Base64Encoder.calcSize(random_bytes_count);
|
||||
|
||||
pub fn cleanup(self: *TmpDir) void {
|
||||
self.dir.close();
|
||||
self.parent_dir.deleteTree(&self.sub_path) catch {};
|
||||
self.parent_dir.close();
|
||||
self.* = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
|
||||
var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
|
||||
std.crypto.randomBytes(&random_bytes) catch
|
||||
@panic("unable to make tmp dir for testing: unable to get random bytes");
|
||||
var sub_path: [TmpDir.sub_path_len]u8 = undefined;
|
||||
std.fs.base64_encoder.encode(&sub_path, &random_bytes);
|
||||
|
||||
var cache_dir = std.fs.cwd().makeOpenPath("zig-cache", .{}) catch
|
||||
@panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");
|
||||
defer cache_dir.close();
|
||||
var parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch
|
||||
@panic("unable to make tmp dir for testing: unable to make and open zig-cache/tmp dir");
|
||||
var dir = parent_dir.makeOpenPath(&sub_path, opts) catch
|
||||
@panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
|
||||
|
||||
return .{
|
||||
.dir = dir,
|
||||
.parent_dir = parent_dir,
|
||||
.sub_path = sub_path,
|
||||
};
|
||||
}
|
||||
|
||||
test "expectEqual nested array" {
|
||||
const a = [2][2]f32{
|
||||
[_]f32{ 1.0, 0.0 },
|
||||
|
|
|
@ -9,6 +9,23 @@ pub const ast = @import("zig/ast.zig");
|
|||
pub const system = @import("zig/system.zig");
|
||||
pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
|
||||
|
||||
pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
|
||||
var line: usize = 0;
|
||||
var column: usize = 0;
|
||||
for (source[0..byte_offset]) |byte| {
|
||||
switch (byte) {
|
||||
'\n' => {
|
||||
line += 1;
|
||||
column = 0;
|
||||
},
|
||||
else => {
|
||||
column += 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
return .{ .line = line, .column = column };
|
||||
}
|
||||
|
||||
test "" {
|
||||
@import("std").meta.refAllDecls(@This());
|
||||
}
|
||||
|
|
|
@ -415,7 +415,12 @@ pub const NativeTargetInfo = struct {
|
|||
// over our own shared objects and find a dynamic linker.
|
||||
self_exe: {
|
||||
const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);
|
||||
defer allocator.free(lib_paths);
|
||||
defer {
|
||||
for (lib_paths) |lib_path| {
|
||||
allocator.free(lib_path);
|
||||
}
|
||||
allocator.free(lib_paths);
|
||||
}
|
||||
|
||||
var found_ld_info: LdInfo = undefined;
|
||||
var found_ld_path: [:0]const u8 = undefined;
|
||||
|
|
|
@ -39,7 +39,7 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.
|
|||
defer function.inst_table.deinit();
|
||||
defer function.errors.deinit();
|
||||
|
||||
for (module_fn.body) |inst| {
|
||||
for (module_fn.body.instructions) |inst| {
|
||||
const new_inst = function.genFuncInst(inst) catch |err| switch (err) {
|
||||
error.CodegenFail => {
|
||||
assert(function.errors.items.len != 0);
|
||||
|
@ -77,32 +77,63 @@ const Function = struct {
|
|||
|
||||
fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {
|
||||
switch (inst.tag) {
|
||||
.unreach => return self.genPanic(inst.src),
|
||||
.breakpoint => return self.genBreakpoint(inst.src),
|
||||
.unreach => return MCValue{ .unreach = {} },
|
||||
.constant => unreachable, // excluded from function bodies
|
||||
.assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
|
||||
.ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
|
||||
.bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
|
||||
.ret => return self.genRet(inst.cast(ir.Inst.Ret).?),
|
||||
.cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),
|
||||
.condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),
|
||||
.isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),
|
||||
.isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?),
|
||||
}
|
||||
}
|
||||
|
||||
fn genPanic(self: *Function, src: usize) !MCValue {
|
||||
// TODO change this to call the panic function
|
||||
fn genBreakpoint(self: *Function, src: usize) !MCValue {
|
||||
switch (self.module.target.cpu.arch) {
|
||||
.i386, .x86_64 => {
|
||||
try self.code.append(0xcc); // int3
|
||||
},
|
||||
else => return self.fail(src, "TODO implement panic for {}", .{self.module.target.cpu.arch}),
|
||||
else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.module.target.cpu.arch}),
|
||||
}
|
||||
return .unreach;
|
||||
}
|
||||
|
||||
fn genRet(self: *Function, src: usize) !void {
|
||||
// TODO change this to call the panic function
|
||||
fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
|
||||
switch (self.module.target.cpu.arch) {
|
||||
.i386, .x86_64 => {
|
||||
try self.code.append(0xc3); // ret
|
||||
},
|
||||
else => return self.fail(src, "TODO implement ret for {}", .{self.module.target.cpu.arch}),
|
||||
else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.module.target.cpu.arch}),
|
||||
}
|
||||
return .unreach;
|
||||
}
|
||||
|
||||
fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue {
|
||||
switch (self.module.target.cpu.arch) {
|
||||
else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.module.target.cpu.arch}),
|
||||
}
|
||||
}
|
||||
|
||||
fn genCondBr(self: *Function, inst: *ir.Inst.CondBr) !MCValue {
|
||||
switch (self.module.target.cpu.arch) {
|
||||
else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.module.target.cpu.arch}),
|
||||
}
|
||||
}
|
||||
|
||||
fn genIsNull(self: *Function, inst: *ir.Inst.IsNull) !MCValue {
|
||||
switch (self.module.target.cpu.arch) {
|
||||
else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.module.target.cpu.arch}),
|
||||
}
|
||||
}
|
||||
|
||||
fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull) !MCValue {
|
||||
// Here you can specialize this instruction if it makes sense to, otherwise the default
|
||||
// will call genIsNull and invert the result.
|
||||
switch (self.module.target.cpu.arch) {
|
||||
else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -501,11 +532,19 @@ fn Reg(comptime arch: Target.Cpu.Arch) type {
|
|||
bh,
|
||||
ch,
|
||||
dh,
|
||||
bph,
|
||||
sph,
|
||||
sih,
|
||||
dih,
|
||||
|
||||
al,
|
||||
bl,
|
||||
cl,
|
||||
dl,
|
||||
bpl,
|
||||
spl,
|
||||
sil,
|
||||
dil,
|
||||
r8b,
|
||||
r9b,
|
||||
r10b,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,8 @@ const std = @import("std");
|
|||
const mem = std.mem;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const BigInt = std.math.big.Int;
|
||||
const BigIntConst = std.math.big.int.Const;
|
||||
const BigIntMutable = std.math.big.int.Mutable;
|
||||
const Type = @import("../type.zig").Type;
|
||||
const Value = @import("../value.zig").Value;
|
||||
const ir = @import("../ir.zig");
|
||||
|
@ -18,6 +19,7 @@ pub const Inst = struct {
|
|||
|
||||
/// These names are used directly as the instruction names in the text format.
|
||||
pub const Tag = enum {
|
||||
breakpoint,
|
||||
str,
|
||||
int,
|
||||
ptrtoint,
|
||||
|
@ -26,6 +28,7 @@ pub const Inst = struct {
|
|||
as,
|
||||
@"asm",
|
||||
@"unreachable",
|
||||
@"return",
|
||||
@"fn",
|
||||
@"export",
|
||||
primitive,
|
||||
|
@ -34,10 +37,15 @@ pub const Inst = struct {
|
|||
bitcast,
|
||||
elemptr,
|
||||
add,
|
||||
cmp,
|
||||
condbr,
|
||||
isnull,
|
||||
isnonnull,
|
||||
};
|
||||
|
||||
pub fn TagToType(tag: Tag) type {
|
||||
return switch (tag) {
|
||||
.breakpoint => Breakpoint,
|
||||
.str => Str,
|
||||
.int => Int,
|
||||
.ptrtoint => PtrToInt,
|
||||
|
@ -46,6 +54,7 @@ pub const Inst = struct {
|
|||
.as => As,
|
||||
.@"asm" => Asm,
|
||||
.@"unreachable" => Unreachable,
|
||||
.@"return" => Return,
|
||||
.@"fn" => Fn,
|
||||
.@"export" => Export,
|
||||
.primitive => Primitive,
|
||||
|
@ -54,6 +63,10 @@ pub const Inst = struct {
|
|||
.bitcast => BitCast,
|
||||
.elemptr => ElemPtr,
|
||||
.add => Add,
|
||||
.cmp => Cmp,
|
||||
.condbr => CondBr,
|
||||
.isnull => IsNull,
|
||||
.isnonnull => IsNonNull,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -64,6 +77,14 @@ pub const Inst = struct {
|
|||
return @fieldParentPtr(T, "base", base);
|
||||
}
|
||||
|
||||
pub const Breakpoint = struct {
|
||||
pub const base_tag = Tag.breakpoint;
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {},
|
||||
kw_args: struct {},
|
||||
};
|
||||
|
||||
pub const Str = struct {
|
||||
pub const base_tag = Tag.str;
|
||||
base: Inst,
|
||||
|
@ -79,7 +100,7 @@ pub const Inst = struct {
|
|||
base: Inst,
|
||||
|
||||
positionals: struct {
|
||||
int: BigInt,
|
||||
int: BigIntConst,
|
||||
},
|
||||
kw_args: struct {},
|
||||
};
|
||||
|
@ -151,19 +172,23 @@ pub const Inst = struct {
|
|||
kw_args: struct {},
|
||||
};
|
||||
|
||||
pub const Return = struct {
|
||||
pub const base_tag = Tag.@"return";
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {},
|
||||
kw_args: struct {},
|
||||
};
|
||||
|
||||
pub const Fn = struct {
|
||||
pub const base_tag = Tag.@"fn";
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {
|
||||
fn_type: *Inst,
|
||||
body: Body,
|
||||
body: Module.Body,
|
||||
},
|
||||
kw_args: struct {},
|
||||
|
||||
pub const Body = struct {
|
||||
instructions: []*Inst,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Export = struct {
|
||||
|
@ -297,6 +322,50 @@ pub const Inst = struct {
|
|||
},
|
||||
kw_args: struct {},
|
||||
};
|
||||
|
||||
pub const Cmp = struct {
|
||||
pub const base_tag = Tag.cmp;
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {
|
||||
lhs: *Inst,
|
||||
op: std.math.CompareOperator,
|
||||
rhs: *Inst,
|
||||
},
|
||||
kw_args: struct {},
|
||||
};
|
||||
|
||||
pub const CondBr = struct {
|
||||
pub const base_tag = Tag.condbr;
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {
|
||||
condition: *Inst,
|
||||
true_body: Module.Body,
|
||||
false_body: Module.Body,
|
||||
},
|
||||
kw_args: struct {},
|
||||
};
|
||||
|
||||
pub const IsNull = struct {
|
||||
pub const base_tag = Tag.isnull;
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {
|
||||
operand: *Inst,
|
||||
},
|
||||
kw_args: struct {},
|
||||
};
|
||||
|
||||
pub const IsNonNull = struct {
|
||||
pub const base_tag = Tag.isnonnull;
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {
|
||||
operand: *Inst,
|
||||
},
|
||||
kw_args: struct {},
|
||||
};
|
||||
};
|
||||
|
||||
pub const ErrorMsg = struct {
|
||||
|
@ -309,6 +378,10 @@ pub const Module = struct {
|
|||
errors: []ErrorMsg,
|
||||
arena: std.heap.ArenaAllocator,
|
||||
|
||||
pub const Body = struct {
|
||||
instructions: []*Inst,
|
||||
};
|
||||
|
||||
pub fn deinit(self: *Module, allocator: *Allocator) void {
|
||||
allocator.free(self.decls);
|
||||
allocator.free(self.errors);
|
||||
|
@ -321,7 +394,7 @@ pub const Module = struct {
|
|||
self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
|
||||
}
|
||||
|
||||
const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Inst.Fn.Body });
|
||||
const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body });
|
||||
|
||||
/// The allocator is used for temporary storage, but this function always returns
|
||||
/// with no resources allocated.
|
||||
|
@ -357,6 +430,7 @@ pub const Module = struct {
|
|||
) @TypeOf(stream).Error!void {
|
||||
// TODO I tried implementing this with an inline for loop and hit a compiler bug
|
||||
switch (decl.tag) {
|
||||
.breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),
|
||||
.str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
|
||||
.int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
|
||||
.ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
|
||||
|
@ -365,6 +439,7 @@ pub const Module = struct {
|
|||
.as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table),
|
||||
.@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
|
||||
.@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
|
||||
.@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table),
|
||||
.@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
|
||||
.@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
|
||||
.primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
|
||||
|
@ -373,6 +448,10 @@ pub const Module = struct {
|
|||
.bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
|
||||
.elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table),
|
||||
.add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table),
|
||||
.cmp => return self.writeInstToStreamGeneric(stream, .cmp, decl, inst_table),
|
||||
.condbr => return self.writeInstToStreamGeneric(stream, .condbr, decl, inst_table),
|
||||
.isnull => return self.writeInstToStreamGeneric(stream, .isnull, decl, inst_table),
|
||||
.isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, decl, inst_table),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -432,7 +511,7 @@ pub const Module = struct {
|
|||
}
|
||||
try stream.writeByte(']');
|
||||
},
|
||||
Inst.Fn.Body => {
|
||||
Module.Body => {
|
||||
try stream.writeAll("{\n");
|
||||
for (param.instructions) |inst, i| {
|
||||
try stream.print(" %{} ", .{i});
|
||||
|
@ -443,7 +522,7 @@ pub const Module = struct {
|
|||
},
|
||||
bool => return stream.writeByte("01"[@boolToInt(param)]),
|
||||
[]u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
|
||||
BigInt => return stream.print("{}", .{param}),
|
||||
BigIntConst => return stream.print("{}", .{param}),
|
||||
else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
|
||||
}
|
||||
}
|
||||
|
@ -497,7 +576,7 @@ const Parser = struct {
|
|||
name_map: std.StringHashMap(usize),
|
||||
};
|
||||
|
||||
fn parseBody(self: *Parser) !Inst.Fn.Body {
|
||||
fn parseBody(self: *Parser) !Module.Body {
|
||||
var body_context = Body{
|
||||
.instructions = std.ArrayList(*Inst).init(self.allocator),
|
||||
.name_map = std.StringHashMap(usize).init(self.allocator),
|
||||
|
@ -532,9 +611,10 @@ const Parser = struct {
|
|||
else => |byte| return self.failByte(byte),
|
||||
};
|
||||
|
||||
return Inst.Fn.Body{
|
||||
.instructions = body_context.instructions.toOwnedSlice(),
|
||||
};
|
||||
// Move the instructions to the arena
|
||||
const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len);
|
||||
mem.copy(*Inst, instrs, body_context.instructions.items);
|
||||
return Module.Body{ .instructions = instrs };
|
||||
}
|
||||
|
||||
fn parseStringLiteral(self: *Parser) ![]u8 {
|
||||
|
@ -565,7 +645,7 @@ const Parser = struct {
|
|||
};
|
||||
}
|
||||
|
||||
fn parseIntegerLiteral(self: *Parser) !BigInt {
|
||||
fn parseIntegerLiteral(self: *Parser) !BigIntConst {
|
||||
const start = self.i;
|
||||
if (self.source[self.i] == '-') self.i += 1;
|
||||
while (true) : (self.i += 1) switch (self.source[self.i]) {
|
||||
|
@ -573,41 +653,46 @@ const Parser = struct {
|
|||
else => break,
|
||||
};
|
||||
const number_text = self.source[start..self.i];
|
||||
var result = try BigInt.init(&self.arena.allocator);
|
||||
result.setString(10, number_text) catch |err| {
|
||||
self.i = start;
|
||||
switch (err) {
|
||||
error.InvalidBase => unreachable,
|
||||
error.InvalidCharForDigit => return self.fail("invalid digit in integer literal", .{}),
|
||||
error.DigitTooLargeForBase => return self.fail("digit too large in integer literal", .{}),
|
||||
else => |e| return e,
|
||||
}
|
||||
const base = 10;
|
||||
// TODO reuse the same array list for this
|
||||
const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
|
||||
const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len);
|
||||
defer self.allocator.free(limbs_buffer);
|
||||
const limb_len = std.math.big.int.calcSetStringLimbCount(base, number_text.len);
|
||||
const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len);
|
||||
var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) {
|
||||
error.InvalidCharacter => {
|
||||
self.i = start;
|
||||
return self.fail("invalid digit in integer literal", .{});
|
||||
},
|
||||
};
|
||||
return result;
|
||||
return result.toConst();
|
||||
}
|
||||
|
||||
fn parseRoot(self: *Parser) !void {
|
||||
// The IR format is designed so that it can be tokenized and parsed at the same time.
|
||||
while (true) : (self.i += 1) switch (self.source[self.i]) {
|
||||
';' => _ = try skipToAndOver(self, '\n'),
|
||||
'@' => {
|
||||
self.i += 1;
|
||||
const ident = try skipToAndOver(self, ' ');
|
||||
skipSpace(self);
|
||||
try requireEatBytes(self, "=");
|
||||
skipSpace(self);
|
||||
const inst = try parseInstruction(self, null);
|
||||
const ident_index = self.decls.items.len;
|
||||
if (try self.global_name_map.put(ident, ident_index)) |_| {
|
||||
return self.fail("redefinition of identifier '{}'", .{ident});
|
||||
}
|
||||
try self.decls.append(inst);
|
||||
continue;
|
||||
},
|
||||
' ', '\n' => continue,
|
||||
0 => break,
|
||||
else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
|
||||
};
|
||||
while (true) {
|
||||
switch (self.source[self.i]) {
|
||||
';' => _ = try skipToAndOver(self, '\n'),
|
||||
'@' => {
|
||||
self.i += 1;
|
||||
const ident = try skipToAndOver(self, ' ');
|
||||
skipSpace(self);
|
||||
try requireEatBytes(self, "=");
|
||||
skipSpace(self);
|
||||
const inst = try parseInstruction(self, null);
|
||||
const ident_index = self.decls.items.len;
|
||||
if (try self.global_name_map.put(ident, ident_index)) |_| {
|
||||
return self.fail("redefinition of identifier '{}'", .{ident});
|
||||
}
|
||||
try self.decls.append(inst);
|
||||
},
|
||||
' ', '\n' => self.i += 1,
|
||||
0 => break,
|
||||
else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eatByte(self: *Parser, byte: u8) bool {
|
||||
|
@ -752,7 +837,7 @@ const Parser = struct {
|
|||
};
|
||||
}
|
||||
switch (T) {
|
||||
Inst.Fn.Body => return parseBody(self),
|
||||
Module.Body => return parseBody(self),
|
||||
bool => {
|
||||
const bool_value = switch (self.source[self.i]) {
|
||||
'0' => false,
|
||||
|
@ -779,7 +864,7 @@ const Parser = struct {
|
|||
},
|
||||
*Inst => return parseParameterInst(self, body_ctx),
|
||||
[]u8, []const u8 => return self.parseStringLiteral(),
|
||||
BigInt => return self.parseIntegerLiteral(),
|
||||
BigIntConst => return self.parseIntegerLiteral(),
|
||||
else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
|
||||
}
|
||||
return self.fail("TODO parse parameter {}", .{@typeName(T)});
|
||||
|
@ -878,11 +963,12 @@ const EmitZIR = struct {
|
|||
}
|
||||
|
||||
fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
|
||||
const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
|
||||
const int_inst = try self.arena.allocator.create(Inst.Int);
|
||||
int_inst.* = .{
|
||||
.base = .{ .src = src, .tag = Inst.Int.base_tag },
|
||||
.positionals = .{
|
||||
.int = try val.toBigInt(&self.arena.allocator),
|
||||
.int = val.toBigInt(big_int_space),
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
|
@ -937,96 +1023,19 @@ const EmitZIR = struct {
|
|||
var instructions = std.ArrayList(*Inst).init(self.allocator);
|
||||
defer instructions.deinit();
|
||||
|
||||
for (module_fn.body) |inst| {
|
||||
const new_inst = switch (inst.tag) {
|
||||
.unreach => blk: {
|
||||
const unreach_inst = try self.arena.allocator.create(Inst.Unreachable);
|
||||
unreach_inst.* = .{
|
||||
.base = .{ .src = inst.src, .tag = Inst.Unreachable.base_tag },
|
||||
.positionals = .{},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &unreach_inst.base;
|
||||
},
|
||||
.constant => unreachable, // excluded from function bodies
|
||||
.assembly => blk: {
|
||||
const old_inst = inst.cast(ir.Inst.Assembly).?;
|
||||
const new_inst = try self.arena.allocator.create(Inst.Asm);
|
||||
|
||||
const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
|
||||
for (inputs) |*elem, i| {
|
||||
elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
|
||||
}
|
||||
|
||||
const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
|
||||
for (clobbers) |*elem, i| {
|
||||
elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
|
||||
}
|
||||
|
||||
const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
|
||||
for (args) |*elem, i| {
|
||||
elem.* = try self.resolveInst(&inst_table, old_inst.args.args[i]);
|
||||
}
|
||||
|
||||
new_inst.* = .{
|
||||
.base = .{ .src = inst.src, .tag = Inst.Asm.base_tag },
|
||||
.positionals = .{
|
||||
.asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
|
||||
.return_type = try self.emitType(inst.src, inst.ty),
|
||||
},
|
||||
.kw_args = .{
|
||||
.@"volatile" = old_inst.args.is_volatile,
|
||||
.output = if (old_inst.args.output) |o|
|
||||
try self.emitStringLiteral(inst.src, o)
|
||||
else
|
||||
null,
|
||||
.inputs = inputs,
|
||||
.clobbers = clobbers,
|
||||
.args = args,
|
||||
},
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
.ptrtoint => blk: {
|
||||
const old_inst = inst.cast(ir.Inst.PtrToInt).?;
|
||||
const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
|
||||
new_inst.* = .{
|
||||
.base = .{ .src = inst.src, .tag = Inst.PtrToInt.base_tag },
|
||||
.positionals = .{
|
||||
.ptr = try self.resolveInst(&inst_table, old_inst.args.ptr),
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
.bitcast => blk: {
|
||||
const old_inst = inst.cast(ir.Inst.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),
|
||||
.operand = try self.resolveInst(&inst_table, old_inst.args.operand),
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
};
|
||||
try instructions.append(new_inst);
|
||||
try inst_table.putNoClobber(inst, new_inst);
|
||||
}
|
||||
try self.emitBody(module_fn.body, &inst_table, &instructions);
|
||||
|
||||
const fn_type = try self.emitType(src, module_fn.fn_type);
|
||||
|
||||
const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
|
||||
mem.copy(*Inst, arena_instrs, instructions.items);
|
||||
|
||||
const fn_inst = try self.arena.allocator.create(Inst.Fn);
|
||||
fn_inst.* = .{
|
||||
.base = .{ .src = src, .tag = Inst.Fn.base_tag },
|
||||
.positionals = .{
|
||||
.fn_type = fn_type,
|
||||
.body = .{
|
||||
.instructions = instructions.toOwnedSlice(),
|
||||
},
|
||||
.body = .{ .instructions = arena_instrs },
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
|
@ -1037,6 +1046,159 @@ const EmitZIR = struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn emitTrivial(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {
|
||||
const new_inst = try self.arena.allocator.create(T);
|
||||
new_inst.* = .{
|
||||
.base = .{ .src = src, .tag = T.base_tag },
|
||||
.positionals = .{},
|
||||
.kw_args = .{},
|
||||
};
|
||||
return &new_inst.base;
|
||||
}
|
||||
|
||||
fn emitBody(
|
||||
self: *EmitZIR,
|
||||
body: ir.Module.Body,
|
||||
inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
|
||||
instructions: *std.ArrayList(*Inst),
|
||||
) Allocator.Error!void {
|
||||
for (body.instructions) |inst| {
|
||||
const new_inst = switch (inst.tag) {
|
||||
.breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
|
||||
.unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
|
||||
.ret => try self.emitTrivial(inst.src, Inst.Return),
|
||||
.constant => unreachable, // excluded from function bodies
|
||||
.assembly => blk: {
|
||||
const old_inst = inst.cast(ir.Inst.Assembly).?;
|
||||
const new_inst = try self.arena.allocator.create(Inst.Asm);
|
||||
|
||||
const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
|
||||
for (inputs) |*elem, i| {
|
||||
elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
|
||||
}
|
||||
|
||||
const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
|
||||
for (clobbers) |*elem, i| {
|
||||
elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
|
||||
}
|
||||
|
||||
const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
|
||||
for (args) |*elem, i| {
|
||||
elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
|
||||
}
|
||||
|
||||
new_inst.* = .{
|
||||
.base = .{ .src = inst.src, .tag = Inst.Asm.base_tag },
|
||||
.positionals = .{
|
||||
.asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
|
||||
.return_type = try self.emitType(inst.src, inst.ty),
|
||||
},
|
||||
.kw_args = .{
|
||||
.@"volatile" = old_inst.args.is_volatile,
|
||||
.output = if (old_inst.args.output) |o|
|
||||
try self.emitStringLiteral(inst.src, o)
|
||||
else
|
||||
null,
|
||||
.inputs = inputs,
|
||||
.clobbers = clobbers,
|
||||
.args = args,
|
||||
},
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
.ptrtoint => blk: {
|
||||
const old_inst = inst.cast(ir.Inst.PtrToInt).?;
|
||||
const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
|
||||
new_inst.* = .{
|
||||
.base = .{ .src = inst.src, .tag = Inst.PtrToInt.base_tag },
|
||||
.positionals = .{
|
||||
.ptr = try self.resolveInst(inst_table, old_inst.args.ptr),
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
.bitcast => blk: {
|
||||
const old_inst = inst.cast(ir.Inst.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),
|
||||
.operand = try self.resolveInst(inst_table, old_inst.args.operand),
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
.cmp => blk: {
|
||||
const old_inst = inst.cast(ir.Inst.Cmp).?;
|
||||
const new_inst = try self.arena.allocator.create(Inst.Cmp);
|
||||
new_inst.* = .{
|
||||
.base = .{ .src = inst.src, .tag = Inst.Cmp.base_tag },
|
||||
.positionals = .{
|
||||
.lhs = try self.resolveInst(inst_table, old_inst.args.lhs),
|
||||
.rhs = try self.resolveInst(inst_table, old_inst.args.rhs),
|
||||
.op = old_inst.args.op,
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
.condbr => blk: {
|
||||
const old_inst = inst.cast(ir.Inst.CondBr).?;
|
||||
|
||||
var true_body = std.ArrayList(*Inst).init(self.allocator);
|
||||
var false_body = std.ArrayList(*Inst).init(self.allocator);
|
||||
|
||||
defer true_body.deinit();
|
||||
defer false_body.deinit();
|
||||
|
||||
try self.emitBody(old_inst.args.true_body, inst_table, &true_body);
|
||||
try self.emitBody(old_inst.args.false_body, inst_table, &false_body);
|
||||
|
||||
const new_inst = try self.arena.allocator.create(Inst.CondBr);
|
||||
new_inst.* = .{
|
||||
.base = .{ .src = inst.src, .tag = Inst.CondBr.base_tag },
|
||||
.positionals = .{
|
||||
.condition = try self.resolveInst(inst_table, old_inst.args.condition),
|
||||
.true_body = .{ .instructions = true_body.toOwnedSlice() },
|
||||
.false_body = .{ .instructions = false_body.toOwnedSlice() },
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
.isnull => blk: {
|
||||
const old_inst = inst.cast(ir.Inst.IsNull).?;
|
||||
const new_inst = try self.arena.allocator.create(Inst.IsNull);
|
||||
new_inst.* = .{
|
||||
.base = .{ .src = inst.src, .tag = Inst.IsNull.base_tag },
|
||||
.positionals = .{
|
||||
.operand = try self.resolveInst(inst_table, old_inst.args.operand),
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
.isnonnull => blk: {
|
||||
const old_inst = inst.cast(ir.Inst.IsNonNull).?;
|
||||
const new_inst = try self.arena.allocator.create(Inst.IsNonNull);
|
||||
new_inst.* = .{
|
||||
.base = .{ .src = inst.src, .tag = Inst.IsNonNull.base_tag },
|
||||
.positionals = .{
|
||||
.operand = try self.resolveInst(inst_table, old_inst.args.operand),
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
};
|
||||
try instructions.append(new_inst);
|
||||
try inst_table.putNoClobber(inst, new_inst);
|
||||
}
|
||||
}
|
||||
|
||||
fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
|
||||
switch (ty.tag()) {
|
||||
.isize => return self.emitPrimitiveType(src, .isize),
|
||||
|
|
|
@ -7,11 +7,6 @@ const fs = std.fs;
|
|||
const elf = std.elf;
|
||||
const codegen = @import("codegen.zig");
|
||||
|
||||
/// On common systems with a 0o022 umask, 0o777 will still result in a file created
|
||||
/// with 0o755 permissions, but it works appropriately if the system is configured
|
||||
/// more leniently. As another data point, C's fopen seems to open files with the
|
||||
/// 666 mode.
|
||||
const executable_mode = 0o777;
|
||||
const default_entry_addr = 0x8000000;
|
||||
|
||||
pub const ErrorMsg = struct {
|
||||
|
@ -35,29 +30,29 @@ pub const Result = struct {
|
|||
/// If incremental linking fails, falls back to truncating the file and rewriting it.
|
||||
/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
|
||||
/// This operation is not atomic.
|
||||
pub fn updateExecutableFilePath(
|
||||
pub fn updateFilePath(
|
||||
allocator: *Allocator,
|
||||
module: ir.Module,
|
||||
dir: fs.Dir,
|
||||
sub_path: []const u8,
|
||||
) !Result {
|
||||
const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = executable_mode });
|
||||
const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(module) });
|
||||
defer file.close();
|
||||
|
||||
return updateExecutableFile(allocator, module, file);
|
||||
return updateFile(allocator, module, file);
|
||||
}
|
||||
|
||||
/// Atomically overwrites the old file, if present.
|
||||
pub fn writeExecutableFilePath(
|
||||
pub fn writeFilePath(
|
||||
allocator: *Allocator,
|
||||
module: ir.Module,
|
||||
dir: fs.Dir,
|
||||
sub_path: []const u8,
|
||||
) !Result {
|
||||
const af = try dir.atomicFile(sub_path, .{ .mode = executable_mode });
|
||||
const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(module) });
|
||||
defer af.deinit();
|
||||
|
||||
const result = try writeExecutableFile(allocator, module, af.file);
|
||||
const result = try writeFile(allocator, module, af.file);
|
||||
try af.finish();
|
||||
return result;
|
||||
}
|
||||
|
@ -67,10 +62,10 @@ pub fn writeExecutableFilePath(
|
|||
/// Returns an error if `file` is not already open with +read +write +seek abilities.
|
||||
/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
|
||||
/// This operation is not atomic.
|
||||
pub fn updateExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
|
||||
return updateExecutableFileInner(allocator, module, file) catch |err| switch (err) {
|
||||
pub fn updateFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
|
||||
return updateFileInner(allocator, module, file) catch |err| switch (err) {
|
||||
error.IncrFailed => {
|
||||
return writeExecutableFile(allocator, module, file);
|
||||
return writeFile(allocator, module, file);
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
|
@ -436,7 +431,7 @@ const Update = struct {
|
|||
},
|
||||
}
|
||||
}
|
||||
if (self.entry_addr == null) {
|
||||
if (self.entry_addr == null and self.module.output_mode == .Exe) {
|
||||
const msg = try std.fmt.allocPrint(self.errors.allocator, "no entry point found", .{});
|
||||
errdefer self.errors.allocator.free(msg);
|
||||
try self.errors.append(.{
|
||||
|
@ -485,7 +480,15 @@ const Update = struct {
|
|||
|
||||
assert(index == 16);
|
||||
|
||||
mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf.ET.EXEC), endian);
|
||||
const elf_type = switch (self.module.output_mode) {
|
||||
.Exe => elf.ET.EXEC,
|
||||
.Obj => elf.ET.REL,
|
||||
.Lib => switch (self.module.link_mode) {
|
||||
.Static => elf.ET.REL,
|
||||
.Dynamic => elf.ET.DYN,
|
||||
},
|
||||
};
|
||||
mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
|
||||
index += 2;
|
||||
|
||||
const machine = self.module.target.cpu.arch.toElfMachine();
|
||||
|
@ -496,10 +499,11 @@ const Update = struct {
|
|||
mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
|
||||
index += 4;
|
||||
|
||||
const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
|
||||
|
||||
switch (ptr_width) {
|
||||
.p32 => {
|
||||
// e_entry
|
||||
mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.entry_addr.?), endian);
|
||||
mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
|
||||
index += 4;
|
||||
|
||||
// e_phoff
|
||||
|
@ -512,7 +516,7 @@ const Update = struct {
|
|||
},
|
||||
.p64 => {
|
||||
// e_entry
|
||||
mem.writeInt(u64, hdr_buf[index..][0..8], self.entry_addr.?, endian);
|
||||
mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
|
||||
index += 8;
|
||||
|
||||
// e_phoff
|
||||
|
@ -750,7 +754,20 @@ const Update = struct {
|
|||
|
||||
/// Truncates the existing file contents and overwrites the contents.
|
||||
/// Returns an error if `file` is not already open with +read +write +seek abilities.
|
||||
pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
|
||||
pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
|
||||
switch (module.output_mode) {
|
||||
.Exe => {},
|
||||
.Obj => {},
|
||||
.Lib => return error.TODOImplementWritingLibFiles,
|
||||
}
|
||||
switch (module.object_format) {
|
||||
.unknown => unreachable, // TODO remove this tag from the enum
|
||||
.coff => return error.TODOImplementWritingCOFF,
|
||||
.elf => {},
|
||||
.macho => return error.TODOImplementWritingMachO,
|
||||
.wasm => return error.TODOImplementWritingWasmObjects,
|
||||
}
|
||||
|
||||
var update = Update{
|
||||
.file = file,
|
||||
.module = &module,
|
||||
|
@ -778,7 +795,7 @@ pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.Fi
|
|||
}
|
||||
|
||||
/// Returns error.IncrFailed if incremental update could not be performed.
|
||||
fn updateExecutableFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
|
||||
fn updateFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
|
||||
//var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
|
||||
|
||||
// TODO implement incremental linking
|
||||
|
@ -822,3 +839,19 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
|
|||
.sh_entsize = @intCast(u32, shdr.sh_entsize),
|
||||
};
|
||||
}
|
||||
|
||||
fn determineMode(module: ir.Module) fs.File.Mode {
|
||||
// On common systems with a 0o022 umask, 0o777 will still result in a file created
|
||||
// with 0o755 permissions, but it works appropriately if the system is configured
|
||||
// more leniently. As another data point, C's fopen seems to open files with the
|
||||
// 666 mode.
|
||||
const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777;
|
||||
switch (module.output_mode) {
|
||||
.Lib => return switch (module.link_mode) {
|
||||
.Dynamic => executable_mode,
|
||||
.Static => fs.File.default_mode,
|
||||
},
|
||||
.Exe => return executable_mode,
|
||||
.Obj => return fs.File.default_mode,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,237 +1,248 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const Target = std.Target;
|
||||
const Compilation = @import("compilation.zig").Compilation;
|
||||
const introspect = @import("introspect.zig");
|
||||
const testing = std.testing;
|
||||
const errmsg = @import("errmsg.zig");
|
||||
const ZigCompiler = @import("compilation.zig").ZigCompiler;
|
||||
const link = @import("link.zig");
|
||||
const ir = @import("ir.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
var ctx: TestContext = undefined;
|
||||
var global_ctx: TestContext = undefined;
|
||||
|
||||
test "stage2" {
|
||||
// TODO provide a way to run tests in evented I/O mode
|
||||
if (!std.io.is_async) return error.SkipZigTest;
|
||||
test "self-hosted" {
|
||||
try global_ctx.init();
|
||||
defer global_ctx.deinit();
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/1364
|
||||
// TODO https://github.com/ziglang/zig/issues/3117
|
||||
if (true) return error.SkipZigTest;
|
||||
try @import("stage2_tests").addCases(&global_ctx);
|
||||
|
||||
try ctx.init();
|
||||
defer ctx.deinit();
|
||||
|
||||
try @import("stage2_tests").addCases(&ctx);
|
||||
|
||||
try ctx.run();
|
||||
try global_ctx.run();
|
||||
}
|
||||
|
||||
const file1 = "1.zig";
|
||||
// TODO https://github.com/ziglang/zig/issues/3783
|
||||
const allocator = std.heap.page_allocator;
|
||||
|
||||
pub const TestContext = struct {
|
||||
zig_compiler: ZigCompiler,
|
||||
zig_lib_dir: []u8,
|
||||
file_index: std.atomic.Int(usize),
|
||||
group: std.event.Group(anyerror!void),
|
||||
any_err: anyerror!void,
|
||||
zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase),
|
||||
zir_transform_cases: std.ArrayList(ZIRTransformCase),
|
||||
|
||||
const tmp_dir_name = "stage2_test_tmp";
|
||||
pub const ZIRCompareOutputCase = struct {
|
||||
name: []const u8,
|
||||
src: [:0]const u8,
|
||||
expected_stdout: []const u8,
|
||||
};
|
||||
|
||||
pub const ZIRTransformCase = struct {
|
||||
name: []const u8,
|
||||
src: [:0]const u8,
|
||||
expected_zir: []const u8,
|
||||
};
|
||||
|
||||
pub fn addZIRCompareOutput(
|
||||
ctx: *TestContext,
|
||||
name: []const u8,
|
||||
src: [:0]const u8,
|
||||
expected_stdout: []const u8,
|
||||
) void {
|
||||
ctx.zir_cmp_output_cases.append(.{
|
||||
.name = name,
|
||||
.src = src,
|
||||
.expected_stdout = expected_stdout,
|
||||
}) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn addZIRTransform(
|
||||
ctx: *TestContext,
|
||||
name: []const u8,
|
||||
src: [:0]const u8,
|
||||
expected_zir: []const u8,
|
||||
) void {
|
||||
ctx.zir_transform_cases.append(.{
|
||||
.name = name,
|
||||
.src = src,
|
||||
.expected_zir = expected_zir,
|
||||
}) catch unreachable;
|
||||
}
|
||||
|
||||
fn init(self: *TestContext) !void {
|
||||
self.* = TestContext{
|
||||
.any_err = {},
|
||||
.zig_compiler = undefined,
|
||||
.zig_lib_dir = undefined,
|
||||
.group = undefined,
|
||||
.file_index = std.atomic.Int(usize).init(0),
|
||||
self.* = .{
|
||||
.zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator),
|
||||
.zir_transform_cases = std.ArrayList(ZIRTransformCase).init(std.heap.page_allocator),
|
||||
};
|
||||
|
||||
self.zig_compiler = try ZigCompiler.init(allocator);
|
||||
errdefer self.zig_compiler.deinit();
|
||||
|
||||
self.group = std.event.Group(anyerror!void).init(allocator);
|
||||
errdefer self.group.wait() catch {};
|
||||
|
||||
self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
|
||||
errdefer allocator.free(self.zig_lib_dir);
|
||||
|
||||
try std.fs.cwd().makePath(tmp_dir_name);
|
||||
errdefer std.fs.cwd().deleteTree(tmp_dir_name) catch {};
|
||||
}
|
||||
|
||||
fn deinit(self: *TestContext) void {
|
||||
std.fs.cwd().deleteTree(tmp_dir_name) catch {};
|
||||
allocator.free(self.zig_lib_dir);
|
||||
self.zig_compiler.deinit();
|
||||
self.zir_cmp_output_cases.deinit();
|
||||
self.zir_transform_cases.deinit();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
fn run(self: *TestContext) !void {
|
||||
std.event.Loop.startCpuBoundOperation();
|
||||
self.any_err = self.group.wait();
|
||||
return self.any_err;
|
||||
var progress = std.Progress{};
|
||||
const root_node = try progress.start("zir", self.zir_cmp_output_cases.items.len +
|
||||
self.zir_transform_cases.items.len);
|
||||
defer root_node.end();
|
||||
|
||||
const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{});
|
||||
|
||||
for (self.zir_cmp_output_cases.items) |case| {
|
||||
std.testing.base_allocator_instance.reset();
|
||||
try self.runOneZIRCmpOutputCase(std.testing.allocator, root_node, case, native_info.target);
|
||||
try std.testing.allocator_instance.validate();
|
||||
}
|
||||
for (self.zir_transform_cases.items) |case| {
|
||||
std.testing.base_allocator_instance.reset();
|
||||
try self.runOneZIRTransformCase(std.testing.allocator, root_node, case, native_info.target);
|
||||
try std.testing.allocator_instance.validate();
|
||||
}
|
||||
}
|
||||
|
||||
fn testCompileError(
|
||||
fn runOneZIRCmpOutputCase(
|
||||
self: *TestContext,
|
||||
source: []const u8,
|
||||
path: []const u8,
|
||||
line: usize,
|
||||
column: usize,
|
||||
msg: []const u8,
|
||||
allocator: *Allocator,
|
||||
root_node: *std.Progress.Node,
|
||||
case: ZIRCompareOutputCase,
|
||||
target: std.Target,
|
||||
) !void {
|
||||
var file_index_buf: [20]u8 = undefined;
|
||||
const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()});
|
||||
const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
|
||||
var tmp = std.testing.tmpDir(.{ .share_with_child_process = true });
|
||||
defer tmp.cleanup();
|
||||
|
||||
if (std.fs.path.dirname(file1_path)) |dirname| {
|
||||
try std.fs.cwd().makePath(dirname);
|
||||
var prg_node = root_node.start(case.name, 4);
|
||||
prg_node.activate();
|
||||
defer prg_node.end();
|
||||
|
||||
var zir_module = x: {
|
||||
var parse_node = prg_node.start("parse", null);
|
||||
parse_node.activate();
|
||||
defer parse_node.end();
|
||||
|
||||
break :x try ir.text.parse(allocator, case.src);
|
||||
};
|
||||
defer zir_module.deinit(allocator);
|
||||
if (zir_module.errors.len != 0) {
|
||||
debugPrintErrors(case.src, zir_module.errors);
|
||||
return error.ParseFailure;
|
||||
}
|
||||
|
||||
try std.fs.cwd().writeFile(file1_path, source);
|
||||
var analyzed_module = x: {
|
||||
var analyze_node = prg_node.start("analyze", null);
|
||||
analyze_node.activate();
|
||||
defer analyze_node.end();
|
||||
|
||||
var comp = try Compilation.create(
|
||||
&self.zig_compiler,
|
||||
"test",
|
||||
file1_path,
|
||||
.Native,
|
||||
.Obj,
|
||||
.Debug,
|
||||
true, // is_static
|
||||
self.zig_lib_dir,
|
||||
);
|
||||
errdefer comp.destroy();
|
||||
break :x try ir.analyze(allocator, zir_module, .{
|
||||
.target = target,
|
||||
.output_mode = .Exe,
|
||||
.link_mode = .Static,
|
||||
.optimize_mode = .Debug,
|
||||
});
|
||||
};
|
||||
defer analyzed_module.deinit(allocator);
|
||||
if (analyzed_module.errors.len != 0) {
|
||||
debugPrintErrors(case.src, analyzed_module.errors);
|
||||
return error.ParseFailure;
|
||||
}
|
||||
|
||||
comp.start();
|
||||
var link_result = x: {
|
||||
var link_node = prg_node.start("link", null);
|
||||
link_node.activate();
|
||||
defer link_node.end();
|
||||
|
||||
try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
|
||||
break :x try link.updateFilePath(allocator, analyzed_module, tmp.dir, "a.out");
|
||||
};
|
||||
defer link_result.deinit(allocator);
|
||||
if (link_result.errors.len != 0) {
|
||||
debugPrintErrors(case.src, link_result.errors);
|
||||
return error.LinkFailure;
|
||||
}
|
||||
|
||||
var exec_result = x: {
|
||||
var exec_node = prg_node.start("execute", null);
|
||||
exec_node.activate();
|
||||
defer exec_node.end();
|
||||
|
||||
break :x try std.ChildProcess.exec(.{
|
||||
.allocator = allocator,
|
||||
.argv = &[_][]const u8{"./a.out"},
|
||||
.cwd_dir = tmp.dir,
|
||||
});
|
||||
};
|
||||
defer allocator.free(exec_result.stdout);
|
||||
defer allocator.free(exec_result.stderr);
|
||||
switch (exec_result.term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
std.debug.warn("elf file exited with code {}\n", .{code});
|
||||
return error.BinaryBadExitCode;
|
||||
}
|
||||
},
|
||||
else => return error.BinaryCrashed,
|
||||
}
|
||||
std.testing.expectEqualSlices(u8, case.expected_stdout, exec_result.stdout);
|
||||
}
|
||||
|
||||
fn testCompareOutputLibC(
|
||||
fn runOneZIRTransformCase(
|
||||
self: *TestContext,
|
||||
source: []const u8,
|
||||
expected_output: []const u8,
|
||||
allocator: *Allocator,
|
||||
root_node: *std.Progress.Node,
|
||||
case: ZIRTransformCase,
|
||||
target: std.Target,
|
||||
) !void {
|
||||
var file_index_buf: [20]u8 = undefined;
|
||||
const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()});
|
||||
const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
|
||||
var prg_node = root_node.start(case.name, 4);
|
||||
prg_node.activate();
|
||||
defer prg_node.end();
|
||||
|
||||
const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() });
|
||||
if (std.fs.path.dirname(file1_path)) |dirname| {
|
||||
try std.fs.cwd().makePath(dirname);
|
||||
var parse_node = prg_node.start("parse", null);
|
||||
parse_node.activate();
|
||||
var zir_module = try ir.text.parse(allocator, case.src);
|
||||
defer zir_module.deinit(allocator);
|
||||
if (zir_module.errors.len != 0) {
|
||||
debugPrintErrors(case.src, zir_module.errors);
|
||||
return error.ParseFailure;
|
||||
}
|
||||
parse_node.end();
|
||||
|
||||
try std.fs.cwd().writeFile(file1_path, source);
|
||||
|
||||
var comp = try Compilation.create(
|
||||
&self.zig_compiler,
|
||||
"test",
|
||||
file1_path,
|
||||
.Native,
|
||||
.Exe,
|
||||
.Debug,
|
||||
false,
|
||||
self.zig_lib_dir,
|
||||
);
|
||||
errdefer comp.destroy();
|
||||
|
||||
_ = try comp.addLinkLib("c", true);
|
||||
comp.link_out_file = output_file;
|
||||
comp.start();
|
||||
|
||||
try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
|
||||
}
|
||||
|
||||
async fn getModuleEventSuccess(
|
||||
comp: *Compilation,
|
||||
exe_file: []const u8,
|
||||
expected_output: []const u8,
|
||||
) anyerror!void {
|
||||
defer comp.destroy();
|
||||
const build_event = comp.events.get();
|
||||
|
||||
switch (build_event) {
|
||||
.Ok => {
|
||||
const argv = [_][]const u8{exe_file};
|
||||
// TODO use event loop
|
||||
const child = try std.ChildProcess.exec(.{
|
||||
.allocator = allocator,
|
||||
.argv = argv,
|
||||
.max_output_bytes = 1024 * 1024,
|
||||
});
|
||||
switch (child.term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
return error.BadReturnCode;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
return error.Crashed;
|
||||
},
|
||||
}
|
||||
if (!mem.eql(u8, child.stdout, expected_output)) {
|
||||
return error.OutputMismatch;
|
||||
}
|
||||
},
|
||||
.Error => @panic("Cannot return error: https://github.com/ziglang/zig/issues/3190"), // |err| return err,
|
||||
.Fail => |msgs| {
|
||||
const stderr = std.io.getStdErr();
|
||||
try stderr.write("build incorrectly failed:\n");
|
||||
for (msgs) |msg| {
|
||||
defer msg.destroy();
|
||||
try msg.printToFile(stderr, .Auto);
|
||||
}
|
||||
},
|
||||
var analyze_node = prg_node.start("analyze", null);
|
||||
analyze_node.activate();
|
||||
var analyzed_module = try ir.analyze(allocator, zir_module, .{
|
||||
.target = target,
|
||||
.output_mode = .Obj,
|
||||
.link_mode = .Static,
|
||||
.optimize_mode = .Debug,
|
||||
});
|
||||
defer analyzed_module.deinit(allocator);
|
||||
if (analyzed_module.errors.len != 0) {
|
||||
debugPrintErrors(case.src, analyzed_module.errors);
|
||||
return error.ParseFailure;
|
||||
}
|
||||
}
|
||||
analyze_node.end();
|
||||
|
||||
async fn getModuleEvent(
|
||||
comp: *Compilation,
|
||||
source: []const u8,
|
||||
path: []const u8,
|
||||
line: usize,
|
||||
column: usize,
|
||||
text: []const u8,
|
||||
) anyerror!void {
|
||||
defer comp.destroy();
|
||||
const build_event = comp.events.get();
|
||||
var emit_node = prg_node.start("emit", null);
|
||||
emit_node.activate();
|
||||
var new_zir_module = try ir.text.emit_zir(allocator, analyzed_module);
|
||||
defer new_zir_module.deinit(allocator);
|
||||
emit_node.end();
|
||||
|
||||
switch (build_event) {
|
||||
.Ok => {
|
||||
@panic("build incorrectly succeeded");
|
||||
},
|
||||
.Error => |err| {
|
||||
@panic("build incorrectly failed");
|
||||
},
|
||||
.Fail => |msgs| {
|
||||
testing.expect(msgs.len != 0);
|
||||
for (msgs) |msg| {
|
||||
if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) {
|
||||
const span = msg.getSpan();
|
||||
const first_token = msg.getTree().tokens.at(span.first);
|
||||
const last_token = msg.getTree().tokens.at(span.first);
|
||||
const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
|
||||
if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
std.debug.warn("\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n", .{
|
||||
source,
|
||||
path,
|
||||
line,
|
||||
column,
|
||||
text,
|
||||
});
|
||||
std.debug.warn("\n====found:========\n", .{});
|
||||
const stderr = std.io.getStdErr();
|
||||
for (msgs) |msg| {
|
||||
defer msg.destroy();
|
||||
try msg.printToFile(stderr, errmsg.Color.Auto);
|
||||
}
|
||||
std.debug.warn("============\n", .{});
|
||||
return error.TestFailed;
|
||||
},
|
||||
}
|
||||
var write_node = prg_node.start("write", null);
|
||||
write_node.activate();
|
||||
var out_zir = std.ArrayList(u8).init(allocator);
|
||||
defer out_zir.deinit();
|
||||
try new_zir_module.writeToStream(allocator, out_zir.outStream());
|
||||
write_node.end();
|
||||
|
||||
std.testing.expectEqualSlices(u8, case.expected_zir, out_zir.items);
|
||||
}
|
||||
};
|
||||
|
||||
fn debugPrintErrors(src: []const u8, errors: var) void {
|
||||
std.debug.warn("\n", .{});
|
||||
var nl = true;
|
||||
var line: usize = 1;
|
||||
for (src) |byte| {
|
||||
if (nl) {
|
||||
std.debug.warn("{: >3}| ", .{line});
|
||||
nl = false;
|
||||
}
|
||||
if (byte == '\n') {
|
||||
nl = true;
|
||||
line += 1;
|
||||
}
|
||||
std.debug.warn("{c}", .{byte});
|
||||
}
|
||||
std.debug.warn("\n", .{});
|
||||
for (errors) |err_msg| {
|
||||
const loc = std.zig.findLineColumn(src, err_msg.byte_offset);
|
||||
std.debug.warn("{}:{}: error: {}\n", .{ loc.line + 1, loc.column + 1, err_msg.msg });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3913,18 +3913,20 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
|
|||
};
|
||||
var aps_int = int;
|
||||
const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int);
|
||||
if (is_negative)
|
||||
aps_int = ZigClangAPSInt_negate(aps_int);
|
||||
var big = try math.big.Int.initCapacity(c.a(), num_limbs);
|
||||
if (is_negative)
|
||||
big.negate();
|
||||
defer big.deinit();
|
||||
if (is_negative) aps_int = ZigClangAPSInt_negate(aps_int);
|
||||
defer if (is_negative) {
|
||||
ZigClangAPSInt_free(aps_int);
|
||||
};
|
||||
|
||||
const limbs = try c.a().alloc(math.big.Limb, num_limbs);
|
||||
defer c.a().free(limbs);
|
||||
|
||||
const data = ZigClangAPSInt_getRawData(aps_int);
|
||||
switch (@sizeOf(std.math.big.Limb)) {
|
||||
switch (@sizeOf(math.big.Limb)) {
|
||||
8 => {
|
||||
var i: usize = 0;
|
||||
while (i < num_limbs) : (i += 1) {
|
||||
big.limbs[i] = data[i];
|
||||
limbs[i] = data[i];
|
||||
}
|
||||
},
|
||||
4 => {
|
||||
|
@ -3934,23 +3936,23 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
|
|||
limb_i += 2;
|
||||
data_i += 1;
|
||||
}) {
|
||||
big.limbs[limb_i] = @truncate(u32, data[data_i]);
|
||||
big.limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32);
|
||||
limbs[limb_i] = @truncate(u32, data[data_i]);
|
||||
limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32);
|
||||
}
|
||||
},
|
||||
else => @compileError("unimplemented"),
|
||||
}
|
||||
const str = big.toString(c.a(), 10, false) catch |err| switch (err) {
|
||||
|
||||
const big: math.big.int.Const = .{ .limbs = limbs, .positive = !is_negative };
|
||||
const str = big.toStringAlloc(c.a(), 10, false) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => unreachable,
|
||||
};
|
||||
defer c.a().free(str);
|
||||
const token = try appendToken(c, .IntegerLiteral, str);
|
||||
const node = try c.a().create(ast.Node.IntegerLiteral);
|
||||
node.* = .{
|
||||
.token = token,
|
||||
};
|
||||
if (is_negative)
|
||||
ZigClangAPSInt_free(aps_int);
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,37 +20,40 @@ pub const Type = extern union {
|
|||
|
||||
pub fn zigTypeTag(self: Type) std.builtin.TypeId {
|
||||
switch (self.tag()) {
|
||||
.@"u8",
|
||||
.@"i8",
|
||||
.@"isize",
|
||||
.@"usize",
|
||||
.@"c_short",
|
||||
.@"c_ushort",
|
||||
.@"c_int",
|
||||
.@"c_uint",
|
||||
.@"c_long",
|
||||
.@"c_ulong",
|
||||
.@"c_longlong",
|
||||
.@"c_ulonglong",
|
||||
.@"c_longdouble",
|
||||
.u8,
|
||||
.i8,
|
||||
.isize,
|
||||
.usize,
|
||||
.c_short,
|
||||
.c_ushort,
|
||||
.c_int,
|
||||
.c_uint,
|
||||
.c_long,
|
||||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.c_longdouble,
|
||||
.int_signed,
|
||||
.int_unsigned,
|
||||
=> return .Int,
|
||||
|
||||
.@"f16",
|
||||
.@"f32",
|
||||
.@"f64",
|
||||
.@"f128",
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
=> return .Float,
|
||||
|
||||
.@"c_void" => return .Opaque,
|
||||
.@"bool" => return .Bool,
|
||||
.@"void" => return .Void,
|
||||
.@"type" => return .Type,
|
||||
.@"anyerror" => return .ErrorSet,
|
||||
.@"comptime_int" => return .ComptimeInt,
|
||||
.@"comptime_float" => return .ComptimeFloat,
|
||||
.@"noreturn" => return .NoReturn,
|
||||
.c_void => return .Opaque,
|
||||
.bool => return .Bool,
|
||||
.void => return .Void,
|
||||
.type => return .Type,
|
||||
.anyerror => return .ErrorSet,
|
||||
.comptime_int => return .ComptimeInt,
|
||||
.comptime_float => return .ComptimeFloat,
|
||||
.noreturn => return .NoReturn,
|
||||
|
||||
.fn_naked_noreturn_no_args => return .Fn,
|
||||
.fn_ccc_void_no_args => return .Fn,
|
||||
|
||||
.array, .array_u8_sentinel_0 => return .Array,
|
||||
.single_const_pointer => return .Pointer,
|
||||
|
@ -153,35 +156,36 @@ pub const Type = extern union {
|
|||
while (true) {
|
||||
const t = ty.tag();
|
||||
switch (t) {
|
||||
.@"u8",
|
||||
.@"i8",
|
||||
.@"isize",
|
||||
.@"usize",
|
||||
.@"c_short",
|
||||
.@"c_ushort",
|
||||
.@"c_int",
|
||||
.@"c_uint",
|
||||
.@"c_long",
|
||||
.@"c_ulong",
|
||||
.@"c_longlong",
|
||||
.@"c_ulonglong",
|
||||
.@"c_longdouble",
|
||||
.@"c_void",
|
||||
.@"f16",
|
||||
.@"f32",
|
||||
.@"f64",
|
||||
.@"f128",
|
||||
.@"bool",
|
||||
.@"void",
|
||||
.@"type",
|
||||
.@"anyerror",
|
||||
.@"comptime_int",
|
||||
.@"comptime_float",
|
||||
.@"noreturn",
|
||||
.u8,
|
||||
.i8,
|
||||
.isize,
|
||||
.usize,
|
||||
.c_short,
|
||||
.c_ushort,
|
||||
.c_int,
|
||||
.c_uint,
|
||||
.c_long,
|
||||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.c_longdouble,
|
||||
.c_void,
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.bool,
|
||||
.void,
|
||||
.type,
|
||||
.anyerror,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.noreturn,
|
||||
=> return out_stream.writeAll(@tagName(t)),
|
||||
|
||||
.const_slice_u8 => return out_stream.writeAll("[]const u8"),
|
||||
.fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
|
||||
.fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
|
||||
.single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
|
||||
|
||||
.array_u8_sentinel_0 => {
|
||||
|
@ -200,6 +204,14 @@ pub const Type = extern union {
|
|||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.int_signed => {
|
||||
const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise);
|
||||
return out_stream.print("i{}", .{payload.bits});
|
||||
},
|
||||
.int_unsigned => {
|
||||
const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise);
|
||||
return out_stream.print("u{}", .{payload.bits});
|
||||
},
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
@ -207,32 +219,33 @@ pub const Type = extern union {
|
|||
|
||||
pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {
|
||||
switch (self.tag()) {
|
||||
.@"u8" => return Value.initTag(.u8_type),
|
||||
.@"i8" => return Value.initTag(.i8_type),
|
||||
.@"isize" => return Value.initTag(.isize_type),
|
||||
.@"usize" => return Value.initTag(.usize_type),
|
||||
.@"c_short" => return Value.initTag(.c_short_type),
|
||||
.@"c_ushort" => return Value.initTag(.c_ushort_type),
|
||||
.@"c_int" => return Value.initTag(.c_int_type),
|
||||
.@"c_uint" => return Value.initTag(.c_uint_type),
|
||||
.@"c_long" => return Value.initTag(.c_long_type),
|
||||
.@"c_ulong" => return Value.initTag(.c_ulong_type),
|
||||
.@"c_longlong" => return Value.initTag(.c_longlong_type),
|
||||
.@"c_ulonglong" => return Value.initTag(.c_ulonglong_type),
|
||||
.@"c_longdouble" => return Value.initTag(.c_longdouble_type),
|
||||
.@"c_void" => return Value.initTag(.c_void_type),
|
||||
.@"f16" => return Value.initTag(.f16_type),
|
||||
.@"f32" => return Value.initTag(.f32_type),
|
||||
.@"f64" => return Value.initTag(.f64_type),
|
||||
.@"f128" => return Value.initTag(.f128_type),
|
||||
.@"bool" => return Value.initTag(.bool_type),
|
||||
.@"void" => return Value.initTag(.void_type),
|
||||
.@"type" => return Value.initTag(.type_type),
|
||||
.@"anyerror" => return Value.initTag(.anyerror_type),
|
||||
.@"comptime_int" => return Value.initTag(.comptime_int_type),
|
||||
.@"comptime_float" => return Value.initTag(.comptime_float_type),
|
||||
.@"noreturn" => return Value.initTag(.noreturn_type),
|
||||
.u8 => return Value.initTag(.u8_type),
|
||||
.i8 => return Value.initTag(.i8_type),
|
||||
.isize => return Value.initTag(.isize_type),
|
||||
.usize => return Value.initTag(.usize_type),
|
||||
.c_short => return Value.initTag(.c_short_type),
|
||||
.c_ushort => return Value.initTag(.c_ushort_type),
|
||||
.c_int => return Value.initTag(.c_int_type),
|
||||
.c_uint => return Value.initTag(.c_uint_type),
|
||||
.c_long => return Value.initTag(.c_long_type),
|
||||
.c_ulong => return Value.initTag(.c_ulong_type),
|
||||
.c_longlong => return Value.initTag(.c_longlong_type),
|
||||
.c_ulonglong => return Value.initTag(.c_ulonglong_type),
|
||||
.c_longdouble => return Value.initTag(.c_longdouble_type),
|
||||
.c_void => return Value.initTag(.c_void_type),
|
||||
.f16 => return Value.initTag(.f16_type),
|
||||
.f32 => return Value.initTag(.f32_type),
|
||||
.f64 => return Value.initTag(.f64_type),
|
||||
.f128 => return Value.initTag(.f128_type),
|
||||
.bool => return Value.initTag(.bool_type),
|
||||
.void => return Value.initTag(.void_type),
|
||||
.type => return Value.initTag(.type_type),
|
||||
.anyerror => return Value.initTag(.anyerror_type),
|
||||
.comptime_int => return Value.initTag(.comptime_int_type),
|
||||
.comptime_float => return Value.initTag(.comptime_float_type),
|
||||
.noreturn => return Value.initTag(.noreturn_type),
|
||||
.fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
|
||||
.fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
|
||||
.single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
|
||||
.const_slice_u8 => return Value.initTag(.const_slice_u8_type),
|
||||
else => {
|
||||
|
@ -245,35 +258,38 @@ pub const Type = extern union {
|
|||
|
||||
pub fn isSinglePointer(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.@"u8",
|
||||
.@"i8",
|
||||
.@"isize",
|
||||
.@"usize",
|
||||
.@"c_short",
|
||||
.@"c_ushort",
|
||||
.@"c_int",
|
||||
.@"c_uint",
|
||||
.@"c_long",
|
||||
.@"c_ulong",
|
||||
.@"c_longlong",
|
||||
.@"c_ulonglong",
|
||||
.@"c_longdouble",
|
||||
.@"f16",
|
||||
.@"f32",
|
||||
.@"f64",
|
||||
.@"f128",
|
||||
.@"c_void",
|
||||
.@"bool",
|
||||
.@"void",
|
||||
.@"type",
|
||||
.@"anyerror",
|
||||
.@"comptime_int",
|
||||
.@"comptime_float",
|
||||
.@"noreturn",
|
||||
.u8,
|
||||
.i8,
|
||||
.isize,
|
||||
.usize,
|
||||
.c_short,
|
||||
.c_ushort,
|
||||
.c_int,
|
||||
.c_uint,
|
||||
.c_long,
|
||||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.c_longdouble,
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_void,
|
||||
.bool,
|
||||
.void,
|
||||
.type,
|
||||
.anyerror,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.noreturn,
|
||||
.array,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
=> false,
|
||||
|
||||
.single_const_pointer,
|
||||
|
@ -284,36 +300,39 @@ pub const Type = extern union {
|
|||
|
||||
pub fn isSlice(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.@"u8",
|
||||
.@"i8",
|
||||
.@"isize",
|
||||
.@"usize",
|
||||
.@"c_short",
|
||||
.@"c_ushort",
|
||||
.@"c_int",
|
||||
.@"c_uint",
|
||||
.@"c_long",
|
||||
.@"c_ulong",
|
||||
.@"c_longlong",
|
||||
.@"c_ulonglong",
|
||||
.@"c_longdouble",
|
||||
.@"f16",
|
||||
.@"f32",
|
||||
.@"f64",
|
||||
.@"f128",
|
||||
.@"c_void",
|
||||
.@"bool",
|
||||
.@"void",
|
||||
.@"type",
|
||||
.@"anyerror",
|
||||
.@"comptime_int",
|
||||
.@"comptime_float",
|
||||
.@"noreturn",
|
||||
.u8,
|
||||
.i8,
|
||||
.isize,
|
||||
.usize,
|
||||
.c_short,
|
||||
.c_ushort,
|
||||
.c_int,
|
||||
.c_uint,
|
||||
.c_long,
|
||||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.c_longdouble,
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_void,
|
||||
.bool,
|
||||
.void,
|
||||
.type,
|
||||
.anyerror,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.noreturn,
|
||||
.array,
|
||||
.array_u8_sentinel_0,
|
||||
.single_const_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
=> false,
|
||||
|
||||
.const_slice_u8 => true,
|
||||
|
@ -323,34 +342,37 @@ pub const Type = extern union {
|
|||
/// Asserts the type is a pointer type.
|
||||
pub fn pointerIsConst(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.@"u8",
|
||||
.@"i8",
|
||||
.@"isize",
|
||||
.@"usize",
|
||||
.@"c_short",
|
||||
.@"c_ushort",
|
||||
.@"c_int",
|
||||
.@"c_uint",
|
||||
.@"c_long",
|
||||
.@"c_ulong",
|
||||
.@"c_longlong",
|
||||
.@"c_ulonglong",
|
||||
.@"c_longdouble",
|
||||
.@"f16",
|
||||
.@"f32",
|
||||
.@"f64",
|
||||
.@"f128",
|
||||
.@"c_void",
|
||||
.@"bool",
|
||||
.@"void",
|
||||
.@"type",
|
||||
.@"anyerror",
|
||||
.@"comptime_int",
|
||||
.@"comptime_float",
|
||||
.@"noreturn",
|
||||
.u8,
|
||||
.i8,
|
||||
.isize,
|
||||
.usize,
|
||||
.c_short,
|
||||
.c_ushort,
|
||||
.c_int,
|
||||
.c_uint,
|
||||
.c_long,
|
||||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.c_longdouble,
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_void,
|
||||
.bool,
|
||||
.void,
|
||||
.type,
|
||||
.anyerror,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.noreturn,
|
||||
.array,
|
||||
.array_u8_sentinel_0,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
=> unreachable,
|
||||
|
||||
.single_const_pointer,
|
||||
|
@ -363,32 +385,35 @@ pub const Type = extern union {
|
|||
/// Asserts the type is a pointer or array type.
|
||||
pub fn elemType(self: Type) Type {
|
||||
return switch (self.tag()) {
|
||||
.@"u8",
|
||||
.@"i8",
|
||||
.@"isize",
|
||||
.@"usize",
|
||||
.@"c_short",
|
||||
.@"c_ushort",
|
||||
.@"c_int",
|
||||
.@"c_uint",
|
||||
.@"c_long",
|
||||
.@"c_ulong",
|
||||
.@"c_longlong",
|
||||
.@"c_ulonglong",
|
||||
.@"c_longdouble",
|
||||
.@"f16",
|
||||
.@"f32",
|
||||
.@"f64",
|
||||
.@"f128",
|
||||
.@"c_void",
|
||||
.@"bool",
|
||||
.@"void",
|
||||
.@"type",
|
||||
.@"anyerror",
|
||||
.@"comptime_int",
|
||||
.@"comptime_float",
|
||||
.@"noreturn",
|
||||
.u8,
|
||||
.i8,
|
||||
.isize,
|
||||
.usize,
|
||||
.c_short,
|
||||
.c_ushort,
|
||||
.c_int,
|
||||
.c_uint,
|
||||
.c_long,
|
||||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.c_longdouble,
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_void,
|
||||
.bool,
|
||||
.void,
|
||||
.type,
|
||||
.anyerror,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.noreturn,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
=> unreachable,
|
||||
|
||||
.array => self.cast(Payload.Array).?.elem_type,
|
||||
|
@ -398,7 +423,7 @@ pub const Type = extern union {
|
|||
};
|
||||
}
|
||||
|
||||
/// Asserts the type is an array.
|
||||
/// Asserts the type is an array or vector.
|
||||
pub fn arrayLen(self: Type) u64 {
|
||||
return switch (self.tag()) {
|
||||
.u8,
|
||||
|
@ -427,9 +452,12 @@ pub const Type = extern union {
|
|||
.comptime_float,
|
||||
.noreturn,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.single_const_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
=> unreachable,
|
||||
|
||||
.array => self.cast(Payload.Array).?.len,
|
||||
|
@ -437,23 +465,67 @@ pub const Type = extern union {
|
|||
};
|
||||
}
|
||||
|
||||
/// Returns true if and only if the type is a fixed-width, signed integer.
|
||||
pub fn isSignedInt(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_longdouble,
|
||||
.c_void,
|
||||
.bool,
|
||||
.void,
|
||||
.type,
|
||||
.anyerror,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.noreturn,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
.int_unsigned,
|
||||
.u8,
|
||||
.usize,
|
||||
.c_ushort,
|
||||
.c_uint,
|
||||
.c_ulong,
|
||||
.c_ulonglong,
|
||||
=> false,
|
||||
|
||||
.int_signed,
|
||||
.i8,
|
||||
.isize,
|
||||
.c_short,
|
||||
.c_int,
|
||||
.c_long,
|
||||
.c_longlong,
|
||||
=> true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the type is a fixed-width integer.
|
||||
pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
|
||||
return switch (self.tag()) {
|
||||
.@"f16",
|
||||
.@"f32",
|
||||
.@"f64",
|
||||
.@"f128",
|
||||
.@"c_longdouble",
|
||||
.@"c_void",
|
||||
.@"bool",
|
||||
.@"void",
|
||||
.@"type",
|
||||
.@"anyerror",
|
||||
.@"comptime_int",
|
||||
.@"comptime_float",
|
||||
.@"noreturn",
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_longdouble,
|
||||
.c_void,
|
||||
.bool,
|
||||
.void,
|
||||
.type,
|
||||
.anyerror,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.noreturn,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
|
@ -461,18 +533,46 @@ pub const Type = extern union {
|
|||
.const_slice_u8,
|
||||
=> unreachable,
|
||||
|
||||
.@"u8" => .{ .signed = false, .bits = 8 },
|
||||
.@"i8" => .{ .signed = true, .bits = 8 },
|
||||
.@"usize" => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
|
||||
.@"isize" => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
|
||||
.@"c_short" => .{ .signed = true, .bits = CInteger.short.sizeInBits(target) },
|
||||
.@"c_ushort" => .{ .signed = false, .bits = CInteger.ushort.sizeInBits(target) },
|
||||
.@"c_int" => .{ .signed = true, .bits = CInteger.int.sizeInBits(target) },
|
||||
.@"c_uint" => .{ .signed = false, .bits = CInteger.uint.sizeInBits(target) },
|
||||
.@"c_long" => .{ .signed = true, .bits = CInteger.long.sizeInBits(target) },
|
||||
.@"c_ulong" => .{ .signed = false, .bits = CInteger.ulong.sizeInBits(target) },
|
||||
.@"c_longlong" => .{ .signed = true, .bits = CInteger.longlong.sizeInBits(target) },
|
||||
.@"c_ulonglong" => .{ .signed = false, .bits = CInteger.ulonglong.sizeInBits(target) },
|
||||
.int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
|
||||
.int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits },
|
||||
.u8 => .{ .signed = false, .bits = 8 },
|
||||
.i8 => .{ .signed = true, .bits = 8 },
|
||||
.usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
|
||||
.isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
|
||||
.c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) },
|
||||
.c_ushort => .{ .signed = false, .bits = CType.ushort.sizeInBits(target) },
|
||||
.c_int => .{ .signed = true, .bits = CType.int.sizeInBits(target) },
|
||||
.c_uint => .{ .signed = false, .bits = CType.uint.sizeInBits(target) },
|
||||
.c_long => .{ .signed = true, .bits = CType.long.sizeInBits(target) },
|
||||
.c_ulong => .{ .signed = false, .bits = CType.ulong.sizeInBits(target) },
|
||||
.c_longlong => .{ .signed = true, .bits = CType.longlong.sizeInBits(target) },
|
||||
.c_ulonglong => .{ .signed = false, .bits = CType.ulonglong.sizeInBits(target) },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isFloat(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_longdouble,
|
||||
=> true,
|
||||
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the type is a fixed-size float.
|
||||
pub fn floatBits(self: Type, target: Target) u16 {
|
||||
return switch (self.tag()) {
|
||||
.f16 => 16,
|
||||
.f32 => 32,
|
||||
.f64 => 64,
|
||||
.f128 => 128,
|
||||
.c_longdouble => CType.longdouble.sizeInBits(target),
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -480,6 +580,7 @@ pub const Type = extern union {
|
|||
pub fn fnParamLen(self: Type) usize {
|
||||
return switch (self.tag()) {
|
||||
.fn_naked_noreturn_no_args => 0,
|
||||
.fn_ccc_void_no_args => 0,
|
||||
|
||||
.f16,
|
||||
.f32,
|
||||
|
@ -511,6 +612,8 @@ pub const Type = extern union {
|
|||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
=> unreachable,
|
||||
};
|
||||
}
|
||||
|
@ -520,6 +623,7 @@ pub const Type = extern union {
|
|||
pub fn fnParamTypes(self: Type, types: []Type) void {
|
||||
switch (self.tag()) {
|
||||
.fn_naked_noreturn_no_args => return,
|
||||
.fn_ccc_void_no_args => return,
|
||||
|
||||
.f16,
|
||||
.f32,
|
||||
|
@ -551,6 +655,8 @@ pub const Type = extern union {
|
|||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
=> unreachable,
|
||||
}
|
||||
}
|
||||
|
@ -559,6 +665,7 @@ pub const Type = extern union {
|
|||
pub fn fnReturnType(self: Type) Type {
|
||||
return switch (self.tag()) {
|
||||
.fn_naked_noreturn_no_args => Type.initTag(.noreturn),
|
||||
.fn_ccc_void_no_args => Type.initTag(.void),
|
||||
|
||||
.f16,
|
||||
.f32,
|
||||
|
@ -590,6 +697,8 @@ pub const Type = extern union {
|
|||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
=> unreachable,
|
||||
};
|
||||
}
|
||||
|
@ -598,6 +707,7 @@ pub const Type = extern union {
|
|||
pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
|
||||
return switch (self.tag()) {
|
||||
.fn_naked_noreturn_no_args => .Naked,
|
||||
.fn_ccc_void_no_args => .C,
|
||||
|
||||
.f16,
|
||||
.f32,
|
||||
|
@ -629,10 +739,148 @@ pub const Type = extern union {
|
|||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
=> unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isNumeric(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_longdouble,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.u8,
|
||||
.i8,
|
||||
.usize,
|
||||
.isize,
|
||||
.c_short,
|
||||
.c_ushort,
|
||||
.c_int,
|
||||
.c_uint,
|
||||
.c_long,
|
||||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
=> true,
|
||||
|
||||
.c_void,
|
||||
.bool,
|
||||
.void,
|
||||
.type,
|
||||
.anyerror,
|
||||
.noreturn,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn onePossibleValue(self: Type) bool {
|
||||
var ty = self;
|
||||
while (true) switch (ty.tag()) {
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_longdouble,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.u8,
|
||||
.i8,
|
||||
.usize,
|
||||
.isize,
|
||||
.c_short,
|
||||
.c_ushort,
|
||||
.c_int,
|
||||
.c_uint,
|
||||
.c_long,
|
||||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.bool,
|
||||
.type,
|
||||
.anyerror,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
=> return false,
|
||||
|
||||
.c_void,
|
||||
.void,
|
||||
.noreturn,
|
||||
=> return true,
|
||||
|
||||
.int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,
|
||||
.int_signed => return ty.cast(Payload.IntSigned).?.bits == 0,
|
||||
.array => {
|
||||
const array = ty.cast(Payload.Array).?;
|
||||
if (array.len == 0)
|
||||
return true;
|
||||
ty = array.elem_type;
|
||||
continue;
|
||||
},
|
||||
.single_const_pointer => {
|
||||
const ptr = ty.cast(Payload.SingleConstPointer).?;
|
||||
ty = ptr.pointee_type;
|
||||
continue;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isCPtr(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_longdouble,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.u8,
|
||||
.i8,
|
||||
.usize,
|
||||
.isize,
|
||||
.c_short,
|
||||
.c_ushort,
|
||||
.c_int,
|
||||
.c_uint,
|
||||
.c_long,
|
||||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.bool,
|
||||
.type,
|
||||
.anyerror,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
.c_void,
|
||||
.void,
|
||||
.noreturn,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
=> return false,
|
||||
};
|
||||
}
|
||||
|
||||
/// This enum does not directly correspond to `std.builtin.TypeId` because
|
||||
/// it has extra enum tags in it, as a way of using less memory. For example,
|
||||
/// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
|
||||
|
@ -667,6 +915,7 @@ pub const Type = extern union {
|
|||
comptime_float,
|
||||
noreturn,
|
||||
fn_naked_noreturn_no_args,
|
||||
fn_ccc_void_no_args,
|
||||
single_const_pointer_to_comptime_int,
|
||||
const_slice_u8, // See last_no_payload_tag below.
|
||||
// After this, the tag requires a payload.
|
||||
|
@ -674,6 +923,8 @@ pub const Type = extern union {
|
|||
array_u8_sentinel_0,
|
||||
array,
|
||||
single_const_pointer,
|
||||
int_signed,
|
||||
int_unsigned,
|
||||
|
||||
pub const last_no_payload_tag = Tag.const_slice_u8;
|
||||
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
|
||||
|
@ -700,10 +951,22 @@ pub const Type = extern union {
|
|||
|
||||
pointee_type: Type,
|
||||
};
|
||||
|
||||
pub const IntSigned = struct {
|
||||
base: Payload = Payload{ .tag = .int_signed },
|
||||
|
||||
bits: u16,
|
||||
};
|
||||
|
||||
pub const IntUnsigned = struct {
|
||||
base: Payload = Payload{ .tag = .int_unsigned },
|
||||
|
||||
bits: u16,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pub const CInteger = enum {
|
||||
pub const CType = enum {
|
||||
short,
|
||||
ushort,
|
||||
int,
|
||||
|
@ -712,8 +975,9 @@ pub const CInteger = enum {
|
|||
ulong,
|
||||
longlong,
|
||||
ulonglong,
|
||||
longdouble,
|
||||
|
||||
pub fn sizeInBits(self: CInteger, target: Target) u16 {
|
||||
pub fn sizeInBits(self: CType, target: Target) u16 {
|
||||
const arch = target.cpu.arch;
|
||||
switch (target.os.tag) {
|
||||
.freestanding, .other => switch (target.cpu.arch) {
|
||||
|
@ -729,6 +993,7 @@ pub const CInteger = enum {
|
|||
.longlong,
|
||||
.ulonglong,
|
||||
=> return 64,
|
||||
.longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
|
||||
},
|
||||
else => switch (self) {
|
||||
.short,
|
||||
|
@ -743,6 +1008,7 @@ pub const CInteger = enum {
|
|||
.longlong,
|
||||
.ulonglong,
|
||||
=> return 64,
|
||||
.longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -767,6 +1033,7 @@ pub const CInteger = enum {
|
|||
.longlong,
|
||||
.ulonglong,
|
||||
=> return 64,
|
||||
.longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
|
||||
},
|
||||
|
||||
.windows, .uefi => switch (self) {
|
||||
|
@ -781,6 +1048,7 @@ pub const CInteger = enum {
|
|||
.longlong,
|
||||
.ulonglong,
|
||||
=> return 64,
|
||||
.longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
|
||||
},
|
||||
|
||||
.ios => switch (self) {
|
||||
|
@ -795,6 +1063,7 @@ pub const CInteger = enum {
|
|||
.longlong,
|
||||
.ulonglong,
|
||||
=> return 64,
|
||||
.longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
|
||||
},
|
||||
|
||||
.ananas,
|
||||
|
@ -821,7 +1090,7 @@ pub const CInteger = enum {
|
|||
.amdpal,
|
||||
.hermit,
|
||||
.hurd,
|
||||
=> @panic("TODO specify the C integer type sizes for this OS"),
|
||||
=> @panic("TODO specify the C integer and float type sizes for this OS"),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,7 +2,8 @@ const std = @import("std");
|
|||
const Type = @import("type.zig").Type;
|
||||
const log2 = std.math.log2;
|
||||
const assert = std.debug.assert;
|
||||
const BigInt = std.math.big.Int;
|
||||
const BigIntConst = std.math.big.int.Const;
|
||||
const BigIntMutable = std.math.big.int.Mutable;
|
||||
const Target = std.Target;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
|
@ -45,12 +46,14 @@ pub const Value = extern union {
|
|||
comptime_float_type,
|
||||
noreturn_type,
|
||||
fn_naked_noreturn_no_args_type,
|
||||
fn_ccc_void_no_args_type,
|
||||
single_const_pointer_to_comptime_int_type,
|
||||
const_slice_u8_type,
|
||||
|
||||
undef,
|
||||
zero,
|
||||
void_value,
|
||||
noreturn_value,
|
||||
the_one_possible_value, // when the type only has one possible value
|
||||
null_value,
|
||||
bool_true,
|
||||
bool_false, // See last_no_payload_tag below.
|
||||
// After this, the tag requires a payload.
|
||||
|
@ -58,11 +61,13 @@ pub const Value = extern union {
|
|||
ty,
|
||||
int_u64,
|
||||
int_i64,
|
||||
int_big,
|
||||
int_big_positive,
|
||||
int_big_negative,
|
||||
function,
|
||||
ref,
|
||||
ref_val,
|
||||
bytes,
|
||||
repeated, // the value is a value repeated some number of times
|
||||
|
||||
pub const last_no_payload_tag = Tag.bool_false;
|
||||
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
|
||||
|
@ -132,18 +137,21 @@ pub const Value = extern union {
|
|||
.comptime_float_type => return out_stream.writeAll("comptime_float"),
|
||||
.noreturn_type => return out_stream.writeAll("noreturn"),
|
||||
.fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
|
||||
.fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
|
||||
.single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
|
||||
.const_slice_u8_type => return out_stream.writeAll("[]const u8"),
|
||||
|
||||
.null_value => return out_stream.writeAll("null"),
|
||||
.undef => return out_stream.writeAll("undefined"),
|
||||
.zero => return out_stream.writeAll("0"),
|
||||
.void_value => return out_stream.writeAll("{}"),
|
||||
.noreturn_value => return out_stream.writeAll("unreachable"),
|
||||
.the_one_possible_value => return out_stream.writeAll("(one possible value)"),
|
||||
.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_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 => return out_stream.print("{}", .{val.cast(Payload.IntBig).?.big_int}),
|
||||
.int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
|
||||
.int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
|
||||
.function => return out_stream.writeAll("(function)"),
|
||||
.ref => return out_stream.writeAll("(ref)"),
|
||||
.ref_val => {
|
||||
|
@ -152,6 +160,10 @@ pub const Value = extern union {
|
|||
continue;
|
||||
},
|
||||
.bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
|
||||
.repeated => {
|
||||
try out_stream.writeAll("(repeated) ");
|
||||
val = val.cast(Payload.Repeated).?.val;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -195,27 +207,31 @@ pub const Value = extern union {
|
|||
.comptime_float_type => Type.initTag(.@"comptime_float"),
|
||||
.noreturn_type => Type.initTag(.@"noreturn"),
|
||||
.fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
|
||||
.fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
|
||||
.single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
|
||||
.const_slice_u8_type => Type.initTag(.const_slice_u8),
|
||||
|
||||
.undef,
|
||||
.zero,
|
||||
.void_value,
|
||||
.noreturn_value,
|
||||
.the_one_possible_value,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.function,
|
||||
.ref,
|
||||
.ref_val,
|
||||
.bytes,
|
||||
.repeated,
|
||||
=> unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the value is an integer.
|
||||
pub fn toBigInt(self: Value, allocator: *Allocator) Allocator.Error!BigInt {
|
||||
pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
|
||||
switch (self.tag()) {
|
||||
.ty,
|
||||
.u8_type,
|
||||
|
@ -244,23 +260,28 @@ pub const Value = extern union {
|
|||
.comptime_float_type,
|
||||
.noreturn_type,
|
||||
.fn_naked_noreturn_no_args_type,
|
||||
.fn_ccc_void_no_args_type,
|
||||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.void_value,
|
||||
.noreturn_value,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
.function,
|
||||
.ref,
|
||||
.ref_val,
|
||||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
=> unreachable,
|
||||
|
||||
.zero => return BigInt.initSet(allocator, 0),
|
||||
.the_one_possible_value, // An integer with one possible value is always zero.
|
||||
.zero,
|
||||
=> return BigIntMutable.init(&space.limbs, 0).toConst(),
|
||||
|
||||
.int_u64 => return BigInt.initSet(allocator, self.cast(Payload.Int_u64).?.int),
|
||||
.int_i64 => return BigInt.initSet(allocator, self.cast(Payload.Int_i64).?.int),
|
||||
.int_big => return self.cast(Payload.IntBig).?.big_int,
|
||||
.int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
|
||||
.int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
|
||||
.int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
|
||||
.int_big_negative => return self.cast(Payload.IntBigPositive).?.asBigInt(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -294,23 +315,90 @@ pub const Value = extern union {
|
|||
.comptime_float_type,
|
||||
.noreturn_type,
|
||||
.fn_naked_noreturn_no_args_type,
|
||||
.fn_ccc_void_no_args_type,
|
||||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.void_value,
|
||||
.noreturn_value,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
.function,
|
||||
.ref,
|
||||
.ref_val,
|
||||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
=> unreachable,
|
||||
|
||||
.zero => return 0,
|
||||
.zero,
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
=> return 0,
|
||||
|
||||
.int_u64 => return self.cast(Payload.Int_u64).?.int,
|
||||
.int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
|
||||
.int_big => return self.cast(Payload.IntBig).?.big_int.to(u64) catch unreachable,
|
||||
.int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
|
||||
.int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(u64) catch 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 {
|
||||
switch (self.tag()) {
|
||||
.ty,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.isize_type,
|
||||
.usize_type,
|
||||
.c_short_type,
|
||||
.c_ushort_type,
|
||||
.c_int_type,
|
||||
.c_uint_type,
|
||||
.c_long_type,
|
||||
.c_ulong_type,
|
||||
.c_longlong_type,
|
||||
.c_ulonglong_type,
|
||||
.c_longdouble_type,
|
||||
.f16_type,
|
||||
.f32_type,
|
||||
.f64_type,
|
||||
.f128_type,
|
||||
.c_void_type,
|
||||
.bool_type,
|
||||
.void_type,
|
||||
.type_type,
|
||||
.anyerror_type,
|
||||
.comptime_int_type,
|
||||
.comptime_float_type,
|
||||
.noreturn_type,
|
||||
.fn_naked_noreturn_no_args_type,
|
||||
.fn_ccc_void_no_args_type,
|
||||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
.function,
|
||||
.ref,
|
||||
.ref_val,
|
||||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
=> unreachable,
|
||||
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
.zero,
|
||||
=> return 0,
|
||||
|
||||
.int_u64 => {
|
||||
const x = self.cast(Payload.Int_u64).?.int;
|
||||
if (x == 0) return 0;
|
||||
return std.math.log2(x) + 1;
|
||||
},
|
||||
.int_i64 => {
|
||||
@panic("TODO implement i64 intBitCountTwosComp");
|
||||
},
|
||||
.int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().bitCountTwosComp(),
|
||||
.int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().bitCountTwosComp(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,19 +432,23 @@ pub const Value = extern union {
|
|||
.comptime_float_type,
|
||||
.noreturn_type,
|
||||
.fn_naked_noreturn_no_args_type,
|
||||
.fn_ccc_void_no_args_type,
|
||||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.void_value,
|
||||
.noreturn_value,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
.function,
|
||||
.ref,
|
||||
.ref_val,
|
||||
.bytes,
|
||||
.repeated,
|
||||
=> unreachable,
|
||||
|
||||
.zero => return true,
|
||||
.zero,
|
||||
.undef,
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
=> return true,
|
||||
|
||||
.int_u64 => switch (ty.zigTypeTag()) {
|
||||
.Int => {
|
||||
|
@ -381,10 +473,18 @@ pub const Value = extern union {
|
|||
.ComptimeInt => return true,
|
||||
else => unreachable,
|
||||
},
|
||||
.int_big => switch (ty.zigTypeTag()) {
|
||||
.int_big_positive => switch (ty.zigTypeTag()) {
|
||||
.Int => {
|
||||
const info = ty.intInfo(target);
|
||||
return self.cast(Payload.IntBig).?.big_int.fitsInTwosComp(info.signed, info.bits);
|
||||
return self.cast(Payload.IntBigPositive).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
|
||||
},
|
||||
.ComptimeInt => return true,
|
||||
else => unreachable,
|
||||
},
|
||||
.int_big_negative => switch (ty.zigTypeTag()) {
|
||||
.Int => {
|
||||
const info = ty.intInfo(target);
|
||||
return self.cast(Payload.IntBigNegative).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
|
||||
},
|
||||
.ComptimeInt => return true,
|
||||
else => unreachable,
|
||||
|
@ -392,9 +492,9 @@ pub const Value = extern union {
|
|||
}
|
||||
}
|
||||
|
||||
/// Asserts the value is a pointer and dereferences it.
|
||||
pub fn pointerDeref(self: Value) Value {
|
||||
switch (self.tag()) {
|
||||
/// Asserts the value is a float
|
||||
pub fn floatHasFraction(self: Value) bool {
|
||||
return switch (self.tag()) {
|
||||
.ty,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
|
@ -422,23 +522,170 @@ pub const Value = extern union {
|
|||
.comptime_float_type,
|
||||
.noreturn_type,
|
||||
.fn_naked_noreturn_no_args_type,
|
||||
.fn_ccc_void_no_args_type,
|
||||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
.function,
|
||||
.ref,
|
||||
.ref_val,
|
||||
.bytes,
|
||||
.repeated,
|
||||
.undef,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.the_one_possible_value,
|
||||
=> unreachable,
|
||||
|
||||
.zero => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn orderAgainstZero(lhs: Value) std.math.Order {
|
||||
switch (lhs.tag()) {
|
||||
.ty,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.isize_type,
|
||||
.usize_type,
|
||||
.c_short_type,
|
||||
.c_ushort_type,
|
||||
.c_int_type,
|
||||
.c_uint_type,
|
||||
.c_long_type,
|
||||
.c_ulong_type,
|
||||
.c_longlong_type,
|
||||
.c_ulonglong_type,
|
||||
.c_longdouble_type,
|
||||
.f16_type,
|
||||
.f32_type,
|
||||
.f64_type,
|
||||
.f128_type,
|
||||
.c_void_type,
|
||||
.bool_type,
|
||||
.void_type,
|
||||
.type_type,
|
||||
.anyerror_type,
|
||||
.comptime_int_type,
|
||||
.comptime_float_type,
|
||||
.noreturn_type,
|
||||
.fn_naked_noreturn_no_args_type,
|
||||
.fn_ccc_void_no_args_type,
|
||||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
.function,
|
||||
.ref,
|
||||
.ref_val,
|
||||
.bytes,
|
||||
.repeated,
|
||||
.undef,
|
||||
=> unreachable,
|
||||
|
||||
.zero,
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
=> return .eq,
|
||||
|
||||
.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),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 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();
|
||||
if (rhs_is_zero) return lhs.orderAgainstZero();
|
||||
|
||||
// TODO floats
|
||||
|
||||
var lhs_bigint_space: BigIntSpace = undefined;
|
||||
var rhs_bigint_space: BigIntSpace = undefined;
|
||||
const lhs_bigint = lhs.toBigInt(&lhs_bigint_space);
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_bigint_space);
|
||||
return lhs_bigint.order(rhs_bigint);
|
||||
}
|
||||
|
||||
/// Asserts the value is comparable.
|
||||
pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value) bool {
|
||||
return order(lhs, rhs).compare(op);
|
||||
}
|
||||
|
||||
/// Asserts the value is comparable.
|
||||
pub fn compareWithZero(lhs: Value, op: std.math.CompareOperator) bool {
|
||||
return orderAgainstZero(lhs).compare(op);
|
||||
}
|
||||
|
||||
pub fn toBool(self: Value) bool {
|
||||
return switch (self.tag()) {
|
||||
.bool_true => true,
|
||||
.bool_false => false,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the value is a pointer and dereferences it.
|
||||
pub fn pointerDeref(self: Value) Value {
|
||||
return switch (self.tag()) {
|
||||
.ty,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.isize_type,
|
||||
.usize_type,
|
||||
.c_short_type,
|
||||
.c_ushort_type,
|
||||
.c_int_type,
|
||||
.c_uint_type,
|
||||
.c_long_type,
|
||||
.c_ulong_type,
|
||||
.c_longlong_type,
|
||||
.c_ulonglong_type,
|
||||
.c_longdouble_type,
|
||||
.f16_type,
|
||||
.f32_type,
|
||||
.f64_type,
|
||||
.f128_type,
|
||||
.c_void_type,
|
||||
.bool_type,
|
||||
.void_type,
|
||||
.type_type,
|
||||
.anyerror_type,
|
||||
.comptime_int_type,
|
||||
.comptime_float_type,
|
||||
.noreturn_type,
|
||||
.fn_naked_noreturn_no_args_type,
|
||||
.fn_ccc_void_no_args_type,
|
||||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.zero,
|
||||
.void_value,
|
||||
.noreturn_value,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
.function,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
=> unreachable,
|
||||
|
||||
.ref => return self.cast(Payload.Ref).?.cell.contents,
|
||||
.ref_val => return self.cast(Payload.RefVal).?.val,
|
||||
}
|
||||
.the_one_possible_value => Value.initTag(.the_one_possible_value),
|
||||
.ref => self.cast(Payload.Ref).?.cell.contents,
|
||||
.ref_val => self.cast(Payload.RefVal).?.val,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the value is a single-item pointer to an array, or an array,
|
||||
|
@ -472,17 +719,20 @@ pub const Value = extern union {
|
|||
.comptime_float_type,
|
||||
.noreturn_type,
|
||||
.fn_naked_noreturn_no_args_type,
|
||||
.fn_ccc_void_no_args_type,
|
||||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.zero,
|
||||
.void_value,
|
||||
.noreturn_value,
|
||||
.the_one_possible_value,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
.function,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.undef,
|
||||
=> unreachable,
|
||||
|
||||
.ref => @panic("TODO figure out how MemoryCell works"),
|
||||
|
@ -493,9 +743,70 @@ pub const Value = extern union {
|
|||
int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
|
||||
return Value.initPayload(&int_payload.base);
|
||||
},
|
||||
|
||||
// No matter the index; all the elements are the same!
|
||||
.repeated => return self.cast(Payload.Repeated).?.val,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isUndef(self: Value) bool {
|
||||
return self.tag() == .undef;
|
||||
}
|
||||
|
||||
/// Valid for all types. Asserts the value is not undefined.
|
||||
/// `.the_one_possible_value` is reported as not null.
|
||||
pub fn isNull(self: Value) bool {
|
||||
return switch (self.tag()) {
|
||||
.ty,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.isize_type,
|
||||
.usize_type,
|
||||
.c_short_type,
|
||||
.c_ushort_type,
|
||||
.c_int_type,
|
||||
.c_uint_type,
|
||||
.c_long_type,
|
||||
.c_ulong_type,
|
||||
.c_longlong_type,
|
||||
.c_ulonglong_type,
|
||||
.c_longdouble_type,
|
||||
.f16_type,
|
||||
.f32_type,
|
||||
.f64_type,
|
||||
.f128_type,
|
||||
.c_void_type,
|
||||
.bool_type,
|
||||
.void_type,
|
||||
.type_type,
|
||||
.anyerror_type,
|
||||
.comptime_int_type,
|
||||
.comptime_float_type,
|
||||
.noreturn_type,
|
||||
.fn_naked_noreturn_no_args_type,
|
||||
.fn_ccc_void_no_args_type,
|
||||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.zero,
|
||||
.the_one_possible_value,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.function,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.ref,
|
||||
.ref_val,
|
||||
.bytes,
|
||||
.repeated,
|
||||
=> false,
|
||||
|
||||
.undef => unreachable,
|
||||
.null_value => true,
|
||||
};
|
||||
}
|
||||
|
||||
/// This type is not copyable since it may contain pointers to its inner data.
|
||||
pub const Payload = struct {
|
||||
tag: Tag,
|
||||
|
@ -510,9 +821,22 @@ pub const Value = extern union {
|
|||
int: i64,
|
||||
};
|
||||
|
||||
pub const IntBig = struct {
|
||||
base: Payload = Payload{ .tag = .int_big },
|
||||
big_int: BigInt,
|
||||
pub const IntBigPositive = struct {
|
||||
base: Payload = Payload{ .tag = .int_big_positive },
|
||||
limbs: []const std.math.big.Limb,
|
||||
|
||||
pub fn asBigInt(self: IntBigPositive) BigIntConst {
|
||||
return BigIntConst{ .limbs = self.limbs, .positive = true };
|
||||
}
|
||||
};
|
||||
|
||||
pub const IntBigNegative = struct {
|
||||
base: Payload = Payload{ .tag = .int_big_negative },
|
||||
limbs: []const std.math.big.Limb,
|
||||
|
||||
pub fn asBigInt(self: IntBigNegative) BigIntConst {
|
||||
return BigIntConst{ .limbs = self.limbs, .positive = false };
|
||||
}
|
||||
};
|
||||
|
||||
pub const Function = struct {
|
||||
|
@ -550,6 +874,20 @@ pub const Value = extern union {
|
|||
base: Payload = Payload{ .tag = .ty },
|
||||
ty: Type,
|
||||
};
|
||||
|
||||
pub const Repeated = struct {
|
||||
base: Payload = Payload{ .tag = .ty },
|
||||
/// This value is repeated some number of times. The amount of times to repeat
|
||||
/// is stored externally.
|
||||
val: Value,
|
||||
};
|
||||
};
|
||||
|
||||
/// Big enough to fit any non-BigInt value
|
||||
pub const BigIntSpace = struct {
|
||||
/// The +1 is headroom so that operations such as incrementing once or decrementing once
|
||||
/// are possible without using an allocator.
|
||||
limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -2,24 +2,27 @@ const std = @import("std");
|
|||
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
|
||||
|
||||
pub fn addCases(ctx: *TestContext) !void {
|
||||
// hello world
|
||||
try ctx.testCompareOutputLibC(
|
||||
\\extern fn puts([*]const u8) void;
|
||||
\\pub export fn main() c_int {
|
||||
\\ puts("Hello, world!");
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "Hello, world!" ++ std.cstr.line_sep);
|
||||
// TODO: re-enable these tests.
|
||||
// https://github.com/ziglang/zig/issues/1364
|
||||
|
||||
// function calling another function
|
||||
try ctx.testCompareOutputLibC(
|
||||
\\extern fn puts(s: [*]const u8) void;
|
||||
\\pub export fn main() c_int {
|
||||
\\ return foo("OK");
|
||||
\\}
|
||||
\\fn foo(s: [*]const u8) c_int {
|
||||
\\ puts(s);
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "OK" ++ std.cstr.line_sep);
|
||||
//// hello world
|
||||
//try ctx.testCompareOutputLibC(
|
||||
// \\extern fn puts([*]const u8) void;
|
||||
// \\pub export fn main() c_int {
|
||||
// \\ puts("Hello, world!");
|
||||
// \\ return 0;
|
||||
// \\}
|
||||
//, "Hello, world!" ++ std.cstr.line_sep);
|
||||
|
||||
//// function calling another function
|
||||
//try ctx.testCompareOutputLibC(
|
||||
// \\extern fn puts(s: [*]const u8) void;
|
||||
// \\pub export fn main() c_int {
|
||||
// \\ return foo("OK");
|
||||
// \\}
|
||||
// \\fn foo(s: [*]const u8) c_int {
|
||||
// \\ puts(s);
|
||||
// \\ return 0;
|
||||
// \\}
|
||||
//, "OK" ++ std.cstr.line_sep);
|
||||
}
|
||||
|
|
|
@ -1,54 +1,57 @@
|
|||
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
|
||||
|
||||
pub fn addCases(ctx: *TestContext) !void {
|
||||
try ctx.testCompileError(
|
||||
\\export fn entry() void {}
|
||||
\\export fn entry() void {}
|
||||
, "1.zig", 2, 8, "exported symbol collision: 'entry'");
|
||||
// TODO: re-enable these tests.
|
||||
// https://github.com/ziglang/zig/issues/1364
|
||||
|
||||
try ctx.testCompileError(
|
||||
\\fn() void {}
|
||||
, "1.zig", 1, 1, "missing function name");
|
||||
//try ctx.testCompileError(
|
||||
// \\export fn entry() void {}
|
||||
// \\export fn entry() void {}
|
||||
//, "1.zig", 2, 8, "exported symbol collision: 'entry'");
|
||||
|
||||
try ctx.testCompileError(
|
||||
\\comptime {
|
||||
\\ return;
|
||||
\\}
|
||||
, "1.zig", 2, 5, "return expression outside function definition");
|
||||
//try ctx.testCompileError(
|
||||
// \\fn() void {}
|
||||
//, "1.zig", 1, 1, "missing function name");
|
||||
|
||||
try ctx.testCompileError(
|
||||
\\export fn entry() void {
|
||||
\\ defer return;
|
||||
\\}
|
||||
, "1.zig", 2, 11, "cannot return from defer expression");
|
||||
//try ctx.testCompileError(
|
||||
// \\comptime {
|
||||
// \\ return;
|
||||
// \\}
|
||||
//, "1.zig", 2, 5, "return expression outside function definition");
|
||||
|
||||
try ctx.testCompileError(
|
||||
\\export fn entry() c_int {
|
||||
\\ return 36893488147419103232;
|
||||
\\}
|
||||
, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
|
||||
//try ctx.testCompileError(
|
||||
// \\export fn entry() void {
|
||||
// \\ defer return;
|
||||
// \\}
|
||||
//, "1.zig", 2, 11, "cannot return from defer expression");
|
||||
|
||||
try ctx.testCompileError(
|
||||
\\comptime {
|
||||
\\ var a: *align(4) align(4) i32 = 0;
|
||||
\\}
|
||||
, "1.zig", 2, 22, "Extra align qualifier");
|
||||
//try ctx.testCompileError(
|
||||
// \\export fn entry() c_int {
|
||||
// \\ return 36893488147419103232;
|
||||
// \\}
|
||||
//, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
|
||||
|
||||
try ctx.testCompileError(
|
||||
\\comptime {
|
||||
\\ var b: *const const i32 = 0;
|
||||
\\}
|
||||
, "1.zig", 2, 19, "Extra align qualifier");
|
||||
//try ctx.testCompileError(
|
||||
// \\comptime {
|
||||
// \\ var a: *align(4) align(4) i32 = 0;
|
||||
// \\}
|
||||
//, "1.zig", 2, 22, "Extra align qualifier");
|
||||
|
||||
try ctx.testCompileError(
|
||||
\\comptime {
|
||||
\\ var c: *volatile volatile i32 = 0;
|
||||
\\}
|
||||
, "1.zig", 2, 22, "Extra align qualifier");
|
||||
//try ctx.testCompileError(
|
||||
// \\comptime {
|
||||
// \\ var b: *const const i32 = 0;
|
||||
// \\}
|
||||
//, "1.zig", 2, 19, "Extra align qualifier");
|
||||
|
||||
try ctx.testCompileError(
|
||||
\\comptime {
|
||||
\\ var d: *allowzero allowzero i32 = 0;
|
||||
\\}
|
||||
, "1.zig", 2, 23, "Extra align qualifier");
|
||||
//try ctx.testCompileError(
|
||||
// \\comptime {
|
||||
// \\ var c: *volatile volatile i32 = 0;
|
||||
// \\}
|
||||
//, "1.zig", 2, 22, "Extra align qualifier");
|
||||
|
||||
//try ctx.testCompileError(
|
||||
// \\comptime {
|
||||
// \\ var d: *allowzero allowzero i32 = 0;
|
||||
// \\}
|
||||
//, "1.zig", 2, 23, "Extra align qualifier");
|
||||
}
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
test "hello world IR" {
|
||||
exeCmp(
|
||||
\\@0 = str("Hello, world!\n")
|
||||
\\@1 = primitive(void)
|
||||
\\@2 = primitive(usize)
|
||||
\\@3 = fntype([], @1, cc=Naked)
|
||||
\\@4 = int(0)
|
||||
\\@5 = int(1)
|
||||
\\@6 = int(231)
|
||||
\\@7 = str("len")
|
||||
\\
|
||||
\\@8 = fn(@3, {
|
||||
\\ %0 = as(@2, @5) ; SYS_write
|
||||
\\ %1 = as(@2, @5) ; STDOUT_FILENO
|
||||
\\ %2 = ptrtoint(@0) ; msg ptr
|
||||
\\ %3 = fieldptr(@0, @7) ; msg len ptr
|
||||
\\ %4 = deref(%3) ; msg len
|
||||
\\ %sysoutreg = str("={rax}")
|
||||
\\ %rax = str("{rax}")
|
||||
\\ %rdi = str("{rdi}")
|
||||
\\ %rsi = str("{rsi}")
|
||||
\\ %rdx = str("{rdx}")
|
||||
\\ %rcx = str("rcx")
|
||||
\\ %r11 = str("r11")
|
||||
\\ %memory = str("memory")
|
||||
\\ %syscall = str("syscall")
|
||||
\\ %5 = asm(%syscall, @2,
|
||||
\\ volatile=1,
|
||||
\\ output=%sysoutreg,
|
||||
\\ inputs=[%rax, %rdi, %rsi, %rdx],
|
||||
\\ clobbers=[%rcx, %r11, %memory],
|
||||
\\ args=[%0, %1, %2, %4])
|
||||
\\
|
||||
\\ %6 = as(@2, @6) ;SYS_exit_group
|
||||
\\ %7 = as(@2, @4) ;exit code
|
||||
\\ %8 = asm(%syscall, @2,
|
||||
\\ volatile=1,
|
||||
\\ output=%sysoutreg,
|
||||
\\ inputs=[%rax, %rdi],
|
||||
\\ clobbers=[%rcx, %r11, %memory],
|
||||
\\ args=[%6, %7])
|
||||
\\
|
||||
\\ %9 = unreachable()
|
||||
\\})
|
||||
\\
|
||||
\\@9 = str("_start")
|
||||
\\@10 = export(@9, @8)
|
||||
,
|
||||
\\Hello, world!
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
fn exeCmp(src: []const u8, expected_stdout: []const u8) void {}
|
|
@ -3,4 +3,5 @@ const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
|
|||
pub fn addCases(ctx: *TestContext) !void {
|
||||
try @import("compile_errors.zig").addCases(ctx);
|
||||
try @import("compare_output.zig").addCases(ctx);
|
||||
@import("zir.zig").addCases(ctx);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
|
||||
|
||||
pub fn addCases(ctx: *TestContext) void {
|
||||
ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint",
|
||||
\\@void = primitive(void)
|
||||
\\@usize = primitive(usize)
|
||||
\\@fnty = fntype([], @void, cc=C)
|
||||
\\@0 = int(0)
|
||||
\\@1 = int(1)
|
||||
\\@2 = int(2)
|
||||
\\@3 = int(3)
|
||||
\\
|
||||
\\@entry = fn(@fnty, {
|
||||
\\ %a = str("\x32\x08\x01\x0a")
|
||||
\\ %eptr0 = elemptr(%a, @0)
|
||||
\\ %eptr1 = elemptr(%a, @1)
|
||||
\\ %eptr2 = elemptr(%a, @2)
|
||||
\\ %eptr3 = elemptr(%a, @3)
|
||||
\\ %v0 = deref(%eptr0)
|
||||
\\ %v1 = deref(%eptr1)
|
||||
\\ %v2 = deref(%eptr2)
|
||||
\\ %v3 = deref(%eptr3)
|
||||
\\ %x0 = add(%v0, %v1)
|
||||
\\ %x1 = add(%v2, %v3)
|
||||
\\ %result = add(%x0, %x1)
|
||||
\\
|
||||
\\ %expected = int(69)
|
||||
\\ %ok = cmp(%result, eq, %expected)
|
||||
\\ %10 = condbr(%ok, {
|
||||
\\ %11 = return()
|
||||
\\ }, {
|
||||
\\ %12 = breakpoint()
|
||||
\\ })
|
||||
\\})
|
||||
\\
|
||||
\\@9 = str("entry")
|
||||
\\@10 = export(@9, @entry)
|
||||
,
|
||||
\\@0 = primitive(void)
|
||||
\\@1 = fntype([], @0, cc=C)
|
||||
\\@2 = fn(@1, {
|
||||
\\ %0 = return()
|
||||
\\})
|
||||
\\@3 = str("entry")
|
||||
\\@4 = export(@3, @2)
|
||||
\\
|
||||
);
|
||||
|
||||
if (@import("std").Target.current.os.tag != .linux or
|
||||
@import("std").Target.current.cpu.arch != .x86_64)
|
||||
{
|
||||
// TODO implement self-hosted PE (.exe file) linking
|
||||
// TODO implement more ZIR so we don't depend on x86_64-linux
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.addZIRCompareOutput("hello world ZIR",
|
||||
\\@0 = str("Hello, world!\n")
|
||||
\\@1 = primitive(noreturn)
|
||||
\\@2 = primitive(usize)
|
||||
\\@3 = fntype([], @1, cc=Naked)
|
||||
\\@4 = int(0)
|
||||
\\@5 = int(1)
|
||||
\\@6 = int(231)
|
||||
\\@7 = str("len")
|
||||
\\
|
||||
\\@8 = fn(@3, {
|
||||
\\ %0 = as(@2, @5) ; SYS_write
|
||||
\\ %1 = as(@2, @5) ; STDOUT_FILENO
|
||||
\\ %2 = ptrtoint(@0) ; msg ptr
|
||||
\\ %3 = fieldptr(@0, @7) ; msg len ptr
|
||||
\\ %4 = deref(%3) ; msg len
|
||||
\\ %sysoutreg = str("={rax}")
|
||||
\\ %rax = str("{rax}")
|
||||
\\ %rdi = str("{rdi}")
|
||||
\\ %rsi = str("{rsi}")
|
||||
\\ %rdx = str("{rdx}")
|
||||
\\ %rcx = str("rcx")
|
||||
\\ %r11 = str("r11")
|
||||
\\ %memory = str("memory")
|
||||
\\ %syscall = str("syscall")
|
||||
\\ %5 = asm(%syscall, @2,
|
||||
\\ volatile=1,
|
||||
\\ output=%sysoutreg,
|
||||
\\ inputs=[%rax, %rdi, %rsi, %rdx],
|
||||
\\ clobbers=[%rcx, %r11, %memory],
|
||||
\\ args=[%0, %1, %2, %4])
|
||||
\\
|
||||
\\ %6 = as(@2, @6) ;SYS_exit_group
|
||||
\\ %7 = as(@2, @4) ;exit code
|
||||
\\ %8 = asm(%syscall, @2,
|
||||
\\ volatile=1,
|
||||
\\ output=%sysoutreg,
|
||||
\\ inputs=[%rax, %rdi],
|
||||
\\ clobbers=[%rcx, %r11, %memory],
|
||||
\\ args=[%6, %7])
|
||||
\\
|
||||
\\ %9 = unreachable()
|
||||
\\})
|
||||
\\
|
||||
\\@9 = str("_start")
|
||||
\\@10 = export(@9, @8)
|
||||
,
|
||||
\\Hello, world!
|
||||
\\
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue