Date: Fri, 1 Feb 2019 17:49:29 -0500
Subject: introduce --single-threaded build option
closes #1764
This adds another boolean to the test matrix; hopefully it does not
inflate the time too much.
std.event.Loop does not work with this option yet. See #1908
---
doc/langref.html.in | 19 +++++++++
src/all_types.hpp | 1 +
src/codegen.cpp | 4 ++
src/main.cpp | 6 +++
std/atomic/queue.zig | 42 ++++++++++++------
std/atomic/stack.zig | 82 ++++++++++++++++++++++++------------
std/event/channel.zig | 3 ++
std/event/future.zig | 3 ++
std/event/group.zig | 3 ++
std/event/lock.zig | 3 ++
std/event/loop.zig | 22 ++++++++++
std/event/net.zig | 3 ++
std/event/rwlock.zig | 3 ++
std/mutex.zig | 66 +++++++++++++++++++++--------
std/os/index.zig | 1 +
std/os/test.zig | 4 ++
std/statically_initialized_mutex.zig | 21 +++++----
test/tests.zig | 43 +++++++++++--------
18 files changed, 246 insertions(+), 83 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/doc/langref.html.in b/doc/langref.html.in
index e192dbbf76..144c8571c4 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -6714,6 +6714,25 @@ pub fn build(b: *Builder) void {
{#header_close#}
{#see_also|Compile Variables|Zig Build System|Undefined Behavior#}
{#header_close#}
+
+ {#header_open|Single Threaded Builds#}
+ Zig has a compile option --single-threaded which has the following effects:
+
+ - {#link|@atomicLoad#} is emitted as a normal load.
+ - {#link|@atomicRmw#} is emitted as a normal memory load, modify, store.
+ - {#link|@fence#} becomes a no-op.
+ - Variables which have Thread Local Storage instead become globals. TODO thread local variables
+ are not implemented yet.
+ - The overhead of {#link|Coroutines#} becomes equivalent to function call overhead.
+ TODO: please note this will not be implemented until the upcoming Coroutine Rewrite
+ - The {#syntax#}@import("builtin").single_threaded{#endsyntax#} becomes {#syntax#}true{#endsyntax#}
+ and therefore various userland APIs which read this variable become more efficient.
+ For example {#syntax#}std.Mutex{#endsyntax#} becomes
+ an empty data structure and all of its functions become no-ops.
+
+
+ {#header_close#}
+
{#header_open|Undefined Behavior#}
Zig has many instances of undefined behavior. If undefined behavior is
diff --git a/src/all_types.hpp b/src/all_types.hpp
index f6fe72891d..3fc6772b31 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1808,6 +1808,7 @@ struct CodeGen {
bool is_static;
bool strip_debug_symbols;
bool is_test_build;
+ bool is_single_threaded;
bool is_native_target;
bool linker_rdynamic;
bool no_rosegment_workaround;
diff --git a/src/codegen.cpp b/src/codegen.cpp
index e2576f5335..b73fda59d1 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -118,6 +118,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
g->string_literals_table.init(16);
g->type_info_cache.init(32);
g->is_test_build = false;
+ g->is_single_threaded = false;
buf_resize(&g->global_asm, 0);
for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) {
@@ -7377,6 +7378,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
buf_appendf(contents, "pub const endian = %s;\n", endian_str);
}
buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build));
+ buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));
buf_appendf(contents, "pub const os = Os.%s;\n", cur_os);
buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch);
buf_appendf(contents, "pub const environ = Environ.%s;\n", cur_environ);
@@ -7411,6 +7413,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
cache_buf(&cache_hash, compiler_id);
cache_int(&cache_hash, g->build_mode);
cache_bool(&cache_hash, g->is_test_build);
+ cache_bool(&cache_hash, g->is_single_threaded);
cache_int(&cache_hash, g->zig_target.arch.arch);
cache_int(&cache_hash, g->zig_target.arch.sub_arch);
cache_int(&cache_hash, g->zig_target.vendor);
@@ -8329,6 +8332,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
cache_bool(ch, g->is_static);
cache_bool(ch, g->strip_debug_symbols);
cache_bool(ch, g->is_test_build);
+ cache_bool(ch, g->is_single_threaded);
cache_bool(ch, g->is_native_target);
cache_bool(ch, g->linker_rdynamic);
cache_bool(ch, g->no_rosegment_workaround);
diff --git a/src/main.cpp b/src/main.cpp
index fd8e3db2fa..81f49089be 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -59,6 +59,7 @@ static int print_full_usage(const char *arg0) {
" --release-fast build with optimizations on and safety off\n"
" --release-safe build with optimizations on and safety on\n"
" --release-small build with size optimizations on and safety off\n"
+ " --single-threaded source may assume it is only used single-threaded\n"
" --static output will be statically linked\n"
" --strip exclude debug symbols\n"
" --target-arch [name] specify target architecture\n"
@@ -393,6 +394,7 @@ int main(int argc, char **argv) {
bool no_rosegment_workaround = false;
bool system_linker_hack = false;
TargetSubsystem subsystem = TargetSubsystemAuto;
+ bool is_single_threaded = false;
if (argc >= 2 && strcmp(argv[1], "build") == 0) {
Buf zig_exe_path_buf = BUF_INIT;
@@ -550,6 +552,8 @@ int main(int argc, char **argv) {
disable_pic = true;
} else if (strcmp(arg, "--system-linker-hack") == 0) {
system_linker_hack = true;
+ } else if (strcmp(arg, "--single-threaded") == 0) {
+ is_single_threaded = true;
} else if (strcmp(arg, "--test-cmd-bin") == 0) {
test_exec_args.append(nullptr);
} else if (arg[1] == 'L' && arg[2] != 0) {
@@ -816,6 +820,7 @@ int main(int argc, char **argv) {
switch (cmd) {
case CmdBuiltin: {
CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir());
+ g->is_single_threaded = is_single_threaded;
Buf *builtin_source = codegen_generate_builtin_source(g);
if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
@@ -889,6 +894,7 @@ int main(int argc, char **argv) {
codegen_set_out_name(g, buf_out_name);
codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
codegen_set_is_test(g, cmd == CmdTest);
+ g->is_single_threaded = is_single_threaded;
codegen_set_linker_script(g, linker_script);
if (each_lib_rpath)
codegen_set_each_lib_rpath(g, each_lib_rpath);
diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig
index 1aab4c32de..6c61bcc048 100644
--- a/std/atomic/queue.zig
+++ b/std/atomic/queue.zig
@@ -170,20 +170,36 @@ test "std.atomic.Queue" {
.get_count = 0,
};
- var putters: [put_thread_count]*std.os.Thread = undefined;
- for (putters) |*t| {
- t.* = try std.os.spawnThread(&context, startPuts);
- }
- var getters: [put_thread_count]*std.os.Thread = undefined;
- for (getters) |*t| {
- t.* = try std.os.spawnThread(&context, startGets);
- }
+ if (builtin.single_threaded) {
+ {
+ var i: usize = 0;
+ while (i < put_thread_count) : (i += 1) {
+ std.debug.assertOrPanic(startPuts(&context) == 0);
+ }
+ }
+ context.puts_done = 1;
+ {
+ var i: usize = 0;
+ while (i < put_thread_count) : (i += 1) {
+ std.debug.assertOrPanic(startGets(&context) == 0);
+ }
+ }
+ } else {
+ var putters: [put_thread_count]*std.os.Thread = undefined;
+ for (putters) |*t| {
+ t.* = try std.os.spawnThread(&context, startPuts);
+ }
+ var getters: [put_thread_count]*std.os.Thread = undefined;
+ for (getters) |*t| {
+ t.* = try std.os.spawnThread(&context, startGets);
+ }
- for (putters) |t|
- t.wait();
- _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
- for (getters) |t|
- t.wait();
+ for (putters) |t|
+ t.wait();
+ _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
+ for (getters) |t|
+ t.wait();
+ }
if (context.put_sum != context.get_sum) {
std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig
index b69a93733c..1e4981353b 100644
--- a/std/atomic/stack.zig
+++ b/std/atomic/stack.zig
@@ -4,10 +4,13 @@ const AtomicOrder = builtin.AtomicOrder;
/// Many reader, many writer, non-allocating, thread-safe
/// Uses a spinlock to protect push() and pop()
+/// When building in single threaded mode, this is a simple linked list.
pub fn Stack(comptime T: type) type {
return struct {
root: ?*Node,
- lock: u8,
+ lock: @typeOf(lock_init),
+
+ const lock_init = if (builtin.single_threaded) {} else u8(0);
pub const Self = @This();
@@ -19,7 +22,7 @@ pub fn Stack(comptime T: type) type {
pub fn init() Self {
return Self{
.root = null,
- .lock = 0,
+ .lock = lock_init,
};
}
@@ -31,20 +34,31 @@ pub fn Stack(comptime T: type) type {
}
pub fn push(self: *Self, node: *Node) void {
- while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
- defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
-
- node.next = self.root;
- self.root = node;
+ if (builtin.single_threaded) {
+ node.next = self.root;
+ self.root = node;
+ } else {
+ while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
+ defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
+
+ node.next = self.root;
+ self.root = node;
+ }
}
pub fn pop(self: *Self) ?*Node {
- while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
- defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
-
- const root = self.root orelse return null;
- self.root = root.next;
- return root;
+ if (builtin.single_threaded) {
+ const root = self.root orelse return null;
+ self.root = root.next;
+ return root;
+ } else {
+ while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
+ defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
+
+ const root = self.root orelse return null;
+ self.root = root.next;
+ return root;
+ }
}
pub fn isEmpty(self: *Self) bool {
@@ -90,20 +104,36 @@ test "std.atomic.stack" {
.get_count = 0,
};
- var putters: [put_thread_count]*std.os.Thread = undefined;
- for (putters) |*t| {
- t.* = try std.os.spawnThread(&context, startPuts);
- }
- var getters: [put_thread_count]*std.os.Thread = undefined;
- for (getters) |*t| {
- t.* = try std.os.spawnThread(&context, startGets);
- }
+ if (builtin.single_threaded) {
+ {
+ var i: usize = 0;
+ while (i < put_thread_count) : (i += 1) {
+ std.debug.assertOrPanic(startPuts(&context) == 0);
+ }
+ }
+ context.puts_done = 1;
+ {
+ var i: usize = 0;
+ while (i < put_thread_count) : (i += 1) {
+ std.debug.assertOrPanic(startGets(&context) == 0);
+ }
+ }
+ } else {
+ var putters: [put_thread_count]*std.os.Thread = undefined;
+ for (putters) |*t| {
+ t.* = try std.os.spawnThread(&context, startPuts);
+ }
+ var getters: [put_thread_count]*std.os.Thread = undefined;
+ for (getters) |*t| {
+ t.* = try std.os.spawnThread(&context, startGets);
+ }
- for (putters) |t|
- t.wait();
- _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
- for (getters) |t|
- t.wait();
+ for (putters) |t|
+ t.wait();
+ _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
+ for (getters) |t|
+ t.wait();
+ }
if (context.put_sum != context.get_sum) {
std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
diff --git a/std/event/channel.zig b/std/event/channel.zig
index 133ce1c69c..f04ad1604e 100644
--- a/std/event/channel.zig
+++ b/std/event/channel.zig
@@ -319,6 +319,9 @@ pub fn Channel(comptime T: type) type {
}
test "std.event.Channel" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
diff --git a/std/event/future.zig b/std/event/future.zig
index d61768b198..55ed01046d 100644
--- a/std/event/future.zig
+++ b/std/event/future.zig
@@ -84,6 +84,9 @@ pub fn Future(comptime T: type) type {
}
test "std.event.Future" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
diff --git a/std/event/group.zig b/std/event/group.zig
index 9f2687a5b3..0e9c2d9655 100644
--- a/std/event/group.zig
+++ b/std/event/group.zig
@@ -121,6 +121,9 @@ pub fn Group(comptime ReturnType: type) type {
}
test "std.event.Group" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
diff --git a/std/event/lock.zig b/std/event/lock.zig
index 46e0d13468..01978e1909 100644
--- a/std/event/lock.zig
+++ b/std/event/lock.zig
@@ -122,6 +122,9 @@ pub const Lock = struct {
};
test "std.event.Lock" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
diff --git a/std/event/loop.zig b/std/event/loop.zig
index 43965e8293..d5228db604 100644
--- a/std/event/loop.zig
+++ b/std/event/loop.zig
@@ -97,6 +97,7 @@ pub const Loop = struct {
/// TODO copy elision / named return values so that the threads referencing *Loop
/// have the correct pointer value.
pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
+ if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode");
const core_count = try os.cpuCount(allocator);
return self.initInternal(allocator, core_count);
}
@@ -201,6 +202,11 @@ pub const Loop = struct {
self.os_data.fs_thread.wait();
}
+ if (builtin.single_threaded) {
+ assert(extra_thread_count == 0);
+ return;
+ }
+
var extra_thread_index: usize = 0;
errdefer {
// writing 8 bytes to an eventfd cannot fail
@@ -301,6 +307,11 @@ pub const Loop = struct {
self.os_data.fs_thread.wait();
}
+ if (builtin.single_threaded) {
+ assert(extra_thread_count == 0);
+ return;
+ }
+
var extra_thread_index: usize = 0;
errdefer {
_ = os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null) catch unreachable;
@@ -338,6 +349,11 @@ pub const Loop = struct {
self.available_eventfd_resume_nodes.push(eventfd_node);
}
+ if (builtin.single_threaded) {
+ assert(extra_thread_count == 0);
+ return;
+ }
+
var extra_thread_index: usize = 0;
errdefer {
var i: usize = 0;
@@ -845,6 +861,9 @@ pub const Loop = struct {
};
test "std.event.Loop - basic" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
@@ -858,6 +877,9 @@ test "std.event.Loop - basic" {
}
test "std.event.Loop - call" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
diff --git a/std/event/net.zig b/std/event/net.zig
index 6838704084..9dac6aa566 100644
--- a/std/event/net.zig
+++ b/std/event/net.zig
@@ -269,6 +269,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !os.File {
}
test "listen on a port, send bytes, receive bytes" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
if (builtin.os != builtin.Os.linux) {
// TODO build abstractions for other operating systems
return error.SkipZigTest;
diff --git a/std/event/rwlock.zig b/std/event/rwlock.zig
index 5d48ea893e..f272ac71ea 100644
--- a/std/event/rwlock.zig
+++ b/std/event/rwlock.zig
@@ -211,6 +211,9 @@ pub const RwLock = struct {
};
test "std.event.RwLock" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
diff --git a/std/mutex.zig b/std/mutex.zig
index 723581cbef..54173fa38a 100644
--- a/std/mutex.zig
+++ b/std/mutex.zig
@@ -14,7 +14,36 @@ const windows = std.os.windows;
/// If you need static initialization, use std.StaticallyInitializedMutex.
/// The Linux implementation is based on mutex3 from
/// https://www.akkadia.org/drepper/futex.pdf
-pub const Mutex = switch(builtin.os) {
+/// When an application is built in single threaded release mode, all the functions are
+/// no-ops. In single threaded debug mode, there is deadlock detection.
+pub const Mutex = if (builtin.single_threaded)
+ struct {
+ lock: @typeOf(lock_init),
+
+ const lock_init = if (std.debug.runtime_safety) false else {};
+
+ pub const Held = struct {
+ mutex: *Mutex,
+
+ pub fn release(self: Held) void {
+ if (std.debug.runtime_safety) {
+ self.mutex.lock = false;
+ }
+ }
+ };
+ pub fn init() Mutex {
+ return Mutex{ .lock = lock_init };
+ }
+ pub fn deinit(self: *Mutex) void {}
+
+ pub fn acquire(self: *Mutex) Held {
+ if (std.debug.runtime_safety and self.lock) {
+ @panic("deadlock detected");
+ }
+ return Held{ .mutex = self };
+ }
+ }
+else switch (builtin.os) {
builtin.Os.linux => struct {
/// 0: unlocked
/// 1: locked, no waiters
@@ -39,9 +68,7 @@ pub const Mutex = switch(builtin.os) {
};
pub fn init() Mutex {
- return Mutex {
- .lock = 0,
- };
+ return Mutex{ .lock = 0 };
}
pub fn deinit(self: *Mutex) void {}
@@ -60,7 +87,7 @@ pub const Mutex = switch(builtin.os) {
}
c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
}
- return Held { .mutex = self };
+ return Held{ .mutex = self };
}
},
// TODO once https://github.com/ziglang/zig/issues/287 (copy elision) is solved, we can make a
@@ -78,21 +105,19 @@ pub const Mutex = switch(builtin.os) {
mutex: *Mutex,
pub fn release(self: Held) void {
- SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock });
+ SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.lock });
}
};
pub fn init() Mutex {
- return Mutex {
- .lock = SpinLock.init(),
- };
+ return Mutex{ .lock = SpinLock.init() };
}
pub fn deinit(self: *Mutex) void {}
pub fn acquire(self: *Mutex) Held {
_ = self.lock.acquire();
- return Held { .mutex = self };
+ return Held{ .mutex = self };
}
},
};
@@ -122,15 +147,20 @@ test "std.Mutex" {
.data = 0,
};
- const thread_count = 10;
- var threads: [thread_count]*std.os.Thread = undefined;
- for (threads) |*t| {
- t.* = try std.os.spawnThread(&context, worker);
- }
- for (threads) |t|
- t.wait();
+ if (builtin.single_threaded) {
+ worker(&context);
+ std.debug.assertOrPanic(context.data == TestContext.incr_count);
+ } else {
+ const thread_count = 10;
+ var threads: [thread_count]*std.os.Thread = undefined;
+ for (threads) |*t| {
+ t.* = try std.os.spawnThread(&context, worker);
+ }
+ for (threads) |t|
+ t.wait();
- std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
+ std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
+ }
}
fn worker(ctx: *TestContext) void {
diff --git a/std/os/index.zig b/std/os/index.zig
index 78d543583d..75abe3bbde 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -3013,6 +3013,7 @@ pub const SpawnThreadError = error{
/// where T is u8, noreturn, void, or !void
/// caller must call wait on the returned thread
pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread {
+ if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode");
// TODO compile-time call graph analysis to determine stack upper bound
// https://github.com/ziglang/zig/issues/157
const default_stack_size = 8 * 1024 * 1024;
diff --git a/std/os/test.zig b/std/os/test.zig
index 5142920687..f14cf47786 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -40,6 +40,8 @@ fn testThreadIdFn(thread_id: *os.Thread.Id) void {
}
test "std.os.Thread.getCurrentId" {
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var thread_current_id: os.Thread.Id = undefined;
const thread = try os.spawnThread(&thread_current_id, testThreadIdFn);
const thread_id = thread.handle();
@@ -53,6 +55,8 @@ test "std.os.Thread.getCurrentId" {
}
test "spawn threads" {
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var shared_ctx: i32 = 1;
const thread1 = try std.os.spawnThread({}, start1);
diff --git a/std/statically_initialized_mutex.zig b/std/statically_initialized_mutex.zig
index dd875eeaf9..37582d49c1 100644
--- a/std/statically_initialized_mutex.zig
+++ b/std/statically_initialized_mutex.zig
@@ -93,13 +93,18 @@ test "std.StaticallyInitializedMutex" {
.data = 0,
};
- const thread_count = 10;
- var threads: [thread_count]*std.os.Thread = undefined;
- for (threads) |*t| {
- t.* = try std.os.spawnThread(&context, TestContext.worker);
- }
- for (threads) |t|
- t.wait();
+ if (builtin.single_threaded) {
+ TestContext.worker(&context);
+ std.debug.assertOrPanic(context.data == TestContext.incr_count);
+ } else {
+ const thread_count = 10;
+ var threads: [thread_count]*std.os.Thread = undefined;
+ for (threads) |*t| {
+ t.* = try std.os.spawnThread(&context, TestContext.worker);
+ }
+ for (threads) |t|
+ t.wait();
- std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
+ std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
+ }
}
diff --git a/test/tests.zig b/test/tests.zig
index 1ca06b4b34..73d4644d18 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -163,25 +163,32 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons
for (test_targets) |test_target| {
const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch);
for (modes) |mode| {
- for ([]bool{
- false,
- true,
- }) |link_libc| {
- if (link_libc and !is_native) {
- // don't assume we have a cross-compiling libc set up
- continue;
- }
- const these_tests = b.addTest(root_src);
- these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", name, @tagName(test_target.os), @tagName(test_target.arch), @tagName(mode), if (link_libc) "c" else "bare"));
- these_tests.setFilter(test_filter);
- these_tests.setBuildMode(mode);
- if (!is_native) {
- these_tests.setTarget(test_target.arch, test_target.os, test_target.environ);
- }
- if (link_libc) {
- these_tests.linkSystemLibrary("c");
+ for ([]bool{ false, true }) |link_libc| {
+ for ([]bool{ false, true }) |single_threaded| {
+ if (link_libc and !is_native) {
+ // don't assume we have a cross-compiling libc set up
+ continue;
+ }
+ const these_tests = b.addTest(root_src);
+ these_tests.setNamePrefix(b.fmt(
+ "{}-{}-{}-{}-{}-{} ",
+ name,
+ @tagName(test_target.os),
+ @tagName(test_target.arch),
+ @tagName(mode),
+ if (link_libc) "c" else "bare",
+ if (single_threaded) "single" else "multi",
+ ));
+ these_tests.setFilter(test_filter);
+ these_tests.setBuildMode(mode);
+ if (!is_native) {
+ these_tests.setTarget(test_target.arch, test_target.os, test_target.environ);
+ }
+ if (link_libc) {
+ these_tests.linkSystemLibrary("c");
+ }
+ step.dependOn(&these_tests.step);
}
- step.dependOn(&these_tests.step);
}
}
}
--
cgit v1.2.3
From 8c6fa982cd0a02775264b616c37da9907cc603bb Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 4 Feb 2019 20:30:00 -0500
Subject: SIMD: array to vector, vector to array, wrapping int add
also vectors and arrays now use the same ConstExprVal representation
See #903
---
src/all_types.hpp | 20 ++-
src/analyze.cpp | 178 ++++++++++++------------
src/analyze.hpp | 1 +
src/codegen.cpp | 98 +++++++++++---
src/ir.cpp | 291 +++++++++++++++++++++++++---------------
src/ir_print.cpp | 18 +++
test/stage1/behavior.zig | 1 +
test/stage1/behavior/vector.zig | 20 +++
8 files changed, 403 insertions(+), 224 deletions(-)
create mode 100644 test/stage1/behavior/vector.zig
(limited to 'src/codegen.cpp')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 3fc6772b31..c4c9e13cfb 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -252,10 +252,6 @@ struct ConstArgTuple {
size_t end_index;
};
-struct ConstVector {
- ConstExprValue *elements;
-};
-
enum ConstValSpecial {
ConstValSpecialRuntime,
ConstValSpecialStatic,
@@ -322,7 +318,6 @@ struct ConstExprValue {
ConstPtrValue x_ptr;
ImportTableEntry *x_import;
ConstArgTuple x_arg_tuple;
- ConstVector x_vector;
// populated if special == ConstValSpecialRuntime
RuntimeHintErrorUnion rh_error_union;
@@ -2239,6 +2234,8 @@ enum IrInstructionId {
IrInstructionIdToBytes,
IrInstructionIdFromBytes,
IrInstructionIdCheckRuntimeScope,
+ IrInstructionIdVectorToArray,
+ IrInstructionIdArrayToVector,
};
struct IrInstruction {
@@ -3368,6 +3365,19 @@ struct IrInstructionBitReverse {
IrInstruction *op;
};
+struct IrInstructionArrayToVector {
+ IrInstruction base;
+
+ IrInstruction *array;
+};
+
+struct IrInstructionVectorToArray {
+ IrInstruction base;
+
+ IrInstruction *vector;
+ LLVMValueRef tmp_ptr;
+};
+
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 99378eb7a8..ff961a7044 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -4457,7 +4457,15 @@ ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
return new_entry;
}
+bool is_valid_vector_elem_type(ZigType *elem_type) {
+ return elem_type->id == ZigTypeIdInt ||
+ elem_type->id == ZigTypeIdFloat ||
+ get_codegen_ptr_type(elem_type) != nullptr;
+}
+
ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) {
+ assert(is_valid_vector_elem_type(elem_type));
+
TypeId type_id = {};
type_id.id = ZigTypeIdVector;
type_id.data.vector.len = len;
@@ -5749,6 +5757,28 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
zig_unreachable();
}
+static bool const_values_equal_array(CodeGen *g, ConstExprValue *a, ConstExprValue *b, size_t len) {
+ assert(a->data.x_array.special != ConstArraySpecialUndef);
+ assert(b->data.x_array.special != ConstArraySpecialUndef);
+ if (a->data.x_array.special == ConstArraySpecialBuf &&
+ b->data.x_array.special == ConstArraySpecialBuf)
+ {
+ return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
+ }
+ expand_undef_array(g, a);
+ expand_undef_array(g, b);
+
+ ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
+ ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
+
+ for (size_t i = 0; i < len; i += 1) {
+ if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
+ return false;
+ }
+
+ return true;
+}
+
bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
assert(a->type->id == b->type->id);
assert(a->special == ConstValSpecialStatic);
@@ -5803,28 +5833,12 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
case ZigTypeIdPointer:
case ZigTypeIdFn:
return const_values_equal_ptr(a, b);
+ case ZigTypeIdVector:
+ assert(a->type->data.vector.len == b->type->data.vector.len);
+ return const_values_equal_array(g, a, b, a->type->data.vector.len);
case ZigTypeIdArray: {
assert(a->type->data.array.len == b->type->data.array.len);
- assert(a->data.x_array.special != ConstArraySpecialUndef);
- assert(b->data.x_array.special != ConstArraySpecialUndef);
- if (a->data.x_array.special == ConstArraySpecialBuf &&
- b->data.x_array.special == ConstArraySpecialBuf)
- {
- return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
- }
- expand_undef_array(g, a);
- expand_undef_array(g, b);
-
- size_t len = a->type->data.array.len;
- ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
- ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
-
- for (size_t i = 0; i < len; i += 1) {
- if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
- return false;
- }
-
- return true;
+ return const_values_equal_array(g, a, b, a->type->data.array.len);
}
case ZigTypeIdStruct:
for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) {
@@ -5853,20 +5867,6 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
case ZigTypeIdArgTuple:
return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index &&
a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index;
- case ZigTypeIdVector: {
- assert(a->type->data.vector.len == b->type->data.vector.len);
-
- size_t len = a->type->data.vector.len;
- ConstExprValue *a_elems = a->data.x_vector.elements;
- ConstExprValue *b_elems = b->data.x_vector.elements;
-
- for (size_t i = 0; i < len; i += 1) {
- if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
- return false;
- }
-
- return true;
- }
case ZigTypeIdBoundFn:
case ZigTypeIdInvalid:
case ZigTypeIdUnreachable:
@@ -5985,6 +5985,40 @@ static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const
}
}
+static void render_const_val_array(CodeGen *g, Buf *buf, ConstExprValue *const_val, size_t len) {
+ switch (const_val->data.x_array.special) {
+ case ConstArraySpecialUndef:
+ buf_append_str(buf, "undefined");
+ return;
+ case ConstArraySpecialBuf: {
+ Buf *array_buf = const_val->data.x_array.data.s_buf;
+ buf_append_char(buf, '"');
+ for (size_t i = 0; i < buf_len(array_buf); i += 1) {
+ uint8_t c = buf_ptr(array_buf)[i];
+ if (c == '"') {
+ buf_append_str(buf, "\\\"");
+ } else {
+ buf_append_char(buf, c);
+ }
+ }
+ buf_append_char(buf, '"');
+ return;
+ }
+ case ConstArraySpecialNone: {
+ buf_appendf(buf, "%s{", buf_ptr(&const_val->type->name));
+ for (uint64_t i = 0; i < len; i += 1) {
+ if (i != 0)
+ buf_appendf(buf, ",");
+ ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i];
+ render_const_value(g, buf, child_value);
+ }
+ buf_appendf(buf, "}");
+ return;
+ }
+ }
+ zig_unreachable();
+}
+
void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
switch (const_val->special) {
case ConstValSpecialRuntime:
@@ -6065,51 +6099,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
}
case ZigTypeIdPointer:
return render_const_val_ptr(g, buf, const_val, type_entry);
+ case ZigTypeIdVector:
+ return render_const_val_array(g, buf, const_val, type_entry->data.vector.len);
case ZigTypeIdArray:
- switch (const_val->data.x_array.special) {
- case ConstArraySpecialUndef:
- buf_append_str(buf, "undefined");
- return;
- case ConstArraySpecialBuf: {
- Buf *array_buf = const_val->data.x_array.data.s_buf;
- buf_append_char(buf, '"');
- for (size_t i = 0; i < buf_len(array_buf); i += 1) {
- uint8_t c = buf_ptr(array_buf)[i];
- if (c == '"') {
- buf_append_str(buf, "\\\"");
- } else {
- buf_append_char(buf, c);
- }
- }
- buf_append_char(buf, '"');
- return;
- }
- case ConstArraySpecialNone: {
- buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
- uint64_t len = type_entry->data.array.len;
- for (uint64_t i = 0; i < len; i += 1) {
- if (i != 0)
- buf_appendf(buf, ",");
- ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i];
- render_const_value(g, buf, child_value);
- }
- buf_appendf(buf, "}");
- return;
- }
- }
- zig_unreachable();
- case ZigTypeIdVector: {
- buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
- uint64_t len = type_entry->data.vector.len;
- for (uint32_t i = 0; i < len; i += 1) {
- if (i != 0)
- buf_appendf(buf, ",");
- ConstExprValue *child_value = &const_val->data.x_vector.elements[i];
- render_const_value(g, buf, child_value);
- }
- buf_appendf(buf, "}");
- return;
- }
+ return render_const_val_array(g, buf, const_val, type_entry->data.array.len);
case ZigTypeIdNull:
{
buf_appendf(buf, "null");
@@ -6379,7 +6372,17 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
// Canonicalize the array value as ConstArraySpecialNone
void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
- assert(const_val->type->id == ZigTypeIdArray);
+ size_t elem_count;
+ ZigType *elem_type;
+ if (const_val->type->id == ZigTypeIdArray) {
+ elem_count = const_val->type->data.array.len;
+ elem_type = const_val->type->data.array.child_type;
+ } else if (const_val->type->id == ZigTypeIdVector) {
+ elem_count = const_val->type->data.vector.len;
+ elem_type = const_val->type->data.vector.elem_type;
+ } else {
+ zig_unreachable();
+ }
if (const_val->special == ConstValSpecialUndef) {
const_val->special = ConstValSpecialStatic;
const_val->data.x_array.special = ConstArraySpecialUndef;
@@ -6389,18 +6392,14 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
return;
case ConstArraySpecialUndef: {
const_val->data.x_array.special = ConstArraySpecialNone;
- size_t elem_count = const_val->type->data.array.len;
const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
for (size_t i = 0; i < elem_count; i += 1) {
ConstExprValue *element_val = &const_val->data.x_array.data.s_none.elements[i];
- element_val->type = const_val->type->data.array.child_type;
+ element_val->type = elem_type;
init_const_undefined(g, element_val);
- ConstParent *parent = get_const_val_parent(g, element_val);
- if (parent != nullptr) {
- parent->id = ConstParentIdArray;
- parent->data.p_array.array_val = const_val;
- parent->data.p_array.elem_index = i;
- }
+ element_val->parent.id = ConstParentIdArray;
+ element_val->parent.data.p_array.array_val = const_val;
+ element_val->parent.data.p_array.elem_index = i;
}
return;
}
@@ -6411,7 +6410,6 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
g->string_literals_table.maybe_remove(buf);
const_val->data.x_array.special = ConstArraySpecialNone;
- size_t elem_count = const_val->type->data.array.len;
assert(elem_count == buf_len(buf));
const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
for (size_t i = 0; i < elem_count; i += 1) {
@@ -6419,6 +6417,9 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
this_char->special = ConstValSpecialStatic;
this_char->type = g->builtin_types.entry_u8;
bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(buf)[i]);
+ this_char->parent.id = ConstParentIdArray;
+ this_char->parent.data.p_array.array_val = const_val;
+ this_char->parent.data.p_array.elem_index = i;
}
return;
}
@@ -6426,6 +6427,7 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
zig_unreachable();
}
+// Deprecated. Reference the parent field directly.
ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) {
return &value->parent;
}
diff --git a/src/analyze.hpp b/src/analyze.hpp
index da5265a594..f558fa44b0 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -74,6 +74,7 @@ TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag);
bool is_ref(ZigType *type_entry);
bool is_array_ref(ZigType *type_entry);
bool is_container_ref(ZigType *type_entry);
+bool is_valid_vector_elem_type(ZigType *elem_type);
void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
void scan_import(CodeGen *g, ImportTableEntry *import);
void preview_use_decl(CodeGen *g, AstNode *node);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index b73fda59d1..de2222afb7 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -1921,9 +1921,8 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) {
}
static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) {
- assert(alignment > 0);
LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name);
- LLVMSetAlignment(result, alignment);
+ LLVMSetAlignment(result, (alignment == 0) ? get_abi_alignment(g, type_entry) : alignment);
return result;
}
@@ -3246,6 +3245,22 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
}
+static bool value_is_all_undef_array(ConstExprValue *const_val, size_t len) {
+ switch (const_val->data.x_array.special) {
+ case ConstArraySpecialUndef:
+ return true;
+ case ConstArraySpecialBuf:
+ return false;
+ case ConstArraySpecialNone:
+ for (size_t i = 0; i < len; i += 1) {
+ if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i]))
+ return false;
+ }
+ return true;
+ }
+ zig_unreachable();
+}
+
static bool value_is_all_undef(ConstExprValue *const_val) {
switch (const_val->special) {
case ConstValSpecialRuntime:
@@ -3260,19 +3275,9 @@ static bool value_is_all_undef(ConstExprValue *const_val) {
}
return true;
} else if (const_val->type->id == ZigTypeIdArray) {
- switch (const_val->data.x_array.special) {
- case ConstArraySpecialUndef:
- return true;
- case ConstArraySpecialBuf:
- return false;
- case ConstArraySpecialNone:
- for (size_t i = 0; i < const_val->type->data.array.len; i += 1) {
- if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i]))
- return false;
- }
- return true;
- }
- zig_unreachable();
+ return value_is_all_undef_array(const_val, const_val->type->data.array.len);
+ } else if (const_val->type->id == ZigTypeIdVector) {
+ return value_is_all_undef_array(const_val, const_val->type->data.vector.len);
} else {
return false;
}
@@ -5194,6 +5199,32 @@ static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutable *executable,
return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
}
+static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutable *executable,
+ IrInstructionVectorToArray *instruction)
+{
+ ZigType *array_type = instruction->base.value.type;
+ assert(array_type->id == ZigTypeIdArray);
+ assert(handle_is_ptr(array_type));
+ assert(instruction->tmp_ptr);
+ LLVMValueRef vector = ir_llvm_value(g, instruction->vector);
+ LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr,
+ LLVMPointerType(instruction->vector->value.type->type_ref, 0), "");
+ gen_store_untyped(g, vector, casted_ptr, 0, false);
+ return instruction->tmp_ptr;
+}
+
+static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executable,
+ IrInstructionArrayToVector *instruction)
+{
+ ZigType *vector_type = instruction->base.value.type;
+ assert(vector_type->id == ZigTypeIdVector);
+ assert(!handle_is_ptr(vector_type));
+ LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array);
+ LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr,
+ LLVMPointerType(vector_type->type_ref, 0), "");
+ return gen_load_untyped(g, casted_ptr, 0, false, "");
+}
+
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
AstNode *source_node = instruction->source_node;
Scope *scope = instruction->scope;
@@ -5439,6 +5470,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_bswap(g, executable, (IrInstructionBswap *)instruction);
case IrInstructionIdBitReverse:
return ir_render_bit_reverse(g, executable, (IrInstructionBitReverse *)instruction);
+ case IrInstructionIdArrayToVector:
+ return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction);
+ case IrInstructionIdVectorToArray:
+ return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction);
}
zig_unreachable();
}
@@ -6016,14 +6051,32 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), true);
}
}
+ zig_unreachable();
}
case ZigTypeIdVector: {
uint32_t len = type_entry->data.vector.len;
- LLVMValueRef *values = allocate(len);
- for (uint32_t i = 0; i < len; i += 1) {
- values[i] = gen_const_val(g, &const_val->data.x_vector.elements[i], "");
+ switch (const_val->data.x_array.special) {
+ case ConstArraySpecialUndef:
+ return LLVMGetUndef(type_entry->type_ref);
+ case ConstArraySpecialNone: {
+ LLVMValueRef *values = allocate(len);
+ for (uint64_t i = 0; i < len; i += 1) {
+ ConstExprValue *elem_value = &const_val->data.x_array.data.s_none.elements[i];
+ values[i] = gen_const_val(g, elem_value, "");
+ }
+ return LLVMConstVector(values, len);
+ }
+ case ConstArraySpecialBuf: {
+ Buf *buf = const_val->data.x_array.data.s_buf;
+ assert(buf_len(buf) == len);
+ LLVMValueRef *values = allocate(len);
+ for (uint64_t i = 0; i < len; i += 1) {
+ values[i] = LLVMConstInt(g->builtin_types.entry_u8->type_ref, buf_ptr(buf)[i], false);
+ }
+ return LLVMConstVector(values, len);
+ }
}
- return LLVMConstVector(values, len);
+ zig_unreachable();
}
case ZigTypeIdUnion:
{
@@ -6467,6 +6520,7 @@ static void do_code_gen(CodeGen *g) {
IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i);
LLVMValueRef *slot;
ZigType *slot_type = instruction->value.type;
+ uint32_t alignment_bytes = 0;
if (instruction->id == IrInstructionIdCast) {
IrInstructionCast *cast_instruction = (IrInstructionCast *)instruction;
slot = &cast_instruction->tmp_ptr;
@@ -6502,10 +6556,14 @@ static void do_code_gen(CodeGen *g) {
} else if (instruction->id == IrInstructionIdCmpxchgGen) {
IrInstructionCmpxchgGen *cmpxchg_instruction = (IrInstructionCmpxchgGen *)instruction;
slot = &cmpxchg_instruction->tmp_ptr;
+ } else if (instruction->id == IrInstructionIdVectorToArray) {
+ IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction;
+ alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type);
+ slot = &vector_to_array_instruction->tmp_ptr;
} else {
zig_unreachable();
}
- *slot = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type));
+ *slot = build_alloca(g, slot_type, "", alignment_bytes);
}
ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base);
diff --git a/src/ir.cpp b/src/ir.cpp
index 3d3c501df3..3cbbdc8103 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -168,6 +168,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed);
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs);
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
+static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry);
static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) {
assert(get_src_ptr_type(const_val->type) != nullptr);
@@ -899,6 +900,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScop
return IrInstructionIdCheckRuntimeScope;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorToArray *) {
+ return IrInstructionIdVectorToArray;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToVector *) {
+ return IrInstructionIdArrayToVector;
+}
+
template
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate(1);
@@ -2821,6 +2830,34 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope,
return &instruction->base;
}
+static IrInstruction *ir_build_vector_to_array(IrAnalyze *ira, IrInstruction *source_instruction,
+ IrInstruction *vector, ZigType *result_type)
+{
+ IrInstructionVectorToArray *instruction = ir_build_instruction(&ira->new_irb,
+ source_instruction->scope, source_instruction->source_node);
+ instruction->base.value.type = result_type;
+ instruction->vector = vector;
+
+ ir_ref_instruction(vector, ira->new_irb.current_basic_block);
+
+ ir_add_alloca(ira, &instruction->base, result_type);
+
+ return &instruction->base;
+}
+
+static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *source_instruction,
+ IrInstruction *array, ZigType *result_type)
+{
+ IrInstructionArrayToVector *instruction = ir_build_instruction(&ira->new_irb,
+ source_instruction->scope, source_instruction->source_node);
+ instruction->base.value.type = result_type;
+ instruction->array = array;
+
+ ir_ref_instruction(array, ira->new_irb.current_basic_block);
+
+ return &instruction->base;
+}
+
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@@ -8270,6 +8307,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc
bool const_val_is_int = (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt);
bool const_val_is_float = (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat);
+ assert(const_val_is_int || const_val_is_float);
if (other_type->id == ZigTypeIdFloat) {
if (const_val->type->id == ZigTypeIdComptimeInt || const_val->type->id == ZigTypeIdComptimeFloat) {
@@ -10714,6 +10752,32 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
}
}
+static IrInstruction *ir_analyze_array_to_vector(IrAnalyze *ira, IrInstruction *source_instr,
+ IrInstruction *array, ZigType *vector_type)
+{
+ if (instr_is_comptime(array)) {
+ // arrays and vectors have the same ConstExprValue representation
+ IrInstruction *result = ir_const(ira, source_instr, vector_type);
+ copy_const_val(&result->value, &array->value, false);
+ result->value.type = vector_type;
+ return result;
+ }
+ return ir_build_array_to_vector(ira, source_instr, array, vector_type);
+}
+
+static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction *source_instr,
+ IrInstruction *vector, ZigType *array_type)
+{
+ if (instr_is_comptime(vector)) {
+ // arrays and vectors have the same ConstExprValue representation
+ IrInstruction *result = ir_const(ira, source_instr, array_type);
+ copy_const_val(&result->value, &vector->value, false);
+ result->value.type = array_type;
+ return result;
+ }
+ return ir_build_vector_to_array(ira, source_instr, vector, array_type);
+}
+
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
ZigType *wanted_type, IrInstruction *value)
{
@@ -11102,6 +11166,23 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
}
}
+ // cast from @Vector(N, T) to [N]T
+ if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdVector &&
+ wanted_type->data.array.len == actual_type->data.vector.len &&
+ types_match_const_cast_only(ira, wanted_type->data.array.child_type,
+ actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)
+ {
+ return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type);
+ }
+
+ // cast from [N]T to @Vector(N, T)
+ if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdVector &&
+ actual_type->data.array.len == wanted_type->data.vector.len &&
+ types_match_const_cast_only(ira, actual_type->data.array.child_type,
+ wanted_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)
+ {
+ return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type);
+ }
// cast from undefined to anything
if (actual_type->id == ZigTypeIdUndefined) {
@@ -11780,8 +11861,8 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
return result;
}
-static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
- IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val)
+static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry,
+ ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val)
{
bool is_int;
bool is_float;
@@ -11803,10 +11884,10 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod ||
op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ)
{
- return ErrorDivByZero;
+ return ir_add_error(ira, source_instr, buf_sprintf("division by zero"));
}
if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) {
- return ErrorNegativeDenominator;
+ return ir_add_error(ira, source_instr, buf_sprintf("negative denominator"));
}
switch (op_id) {
@@ -11852,7 +11933,7 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
BigInt orig_bigint;
bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint);
if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) {
- return ErrorShiftedOutOneBits;
+ return ir_add_error(ira, source_instr, buf_sprintf("exact shift shifted out 1 bits"));
}
break;
}
@@ -11920,14 +12001,14 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
BigInt remainder;
bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
if (bigint_cmp_zero(&remainder) != CmpEQ) {
- return ErrorExactDivRemainder;
+ return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder"));
}
} else {
float_div_trunc(out_val, op1_val, op2_val);
ConstExprValue remainder;
float_rem(&remainder, op1_val, op2_val);
if (float_cmp_zero(&remainder) != CmpEQ) {
- return ErrorExactDivRemainder;
+ return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder"));
}
}
break;
@@ -11951,13 +12032,51 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count,
type_entry->data.integral.is_signed))
{
- return ErrorOverflow;
+ return ir_add_error(ira, source_instr, buf_sprintf("operation caused overflow"));
}
}
out_val->type = type_entry;
out_val->special = ConstValSpecialStatic;
- return 0;
+ return nullptr;
+}
+
+// This works on operands that have already been checked to be comptime known.
+static IrInstruction *ir_analyze_math_op(IrAnalyze *ira, IrInstruction *source_instr,
+ ZigType *type_entry, ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val)
+{
+ IrInstruction *result_instruction = ir_const(ira, source_instr, type_entry);
+ ConstExprValue *out_val = &result_instruction->value;
+ if (type_entry->id == ZigTypeIdVector) {
+ expand_undef_array(ira->codegen, op1_val);
+ expand_undef_array(ira->codegen, op2_val);
+ out_val->special = ConstValSpecialUndef;
+ expand_undef_array(ira->codegen, out_val);
+ size_t len = type_entry->data.vector.len;
+ ZigType *scalar_type = type_entry->data.vector.elem_type;
+ for (size_t i = 0; i < len; i += 1) {
+ ConstExprValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i];
+ ConstExprValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i];
+ ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i];
+ assert(scalar_op1_val->type == scalar_type);
+ assert(scalar_op2_val->type == scalar_type);
+ assert(scalar_out_val->type == scalar_type);
+ ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type,
+ scalar_op1_val, op_id, scalar_op2_val, scalar_out_val);
+ if (msg != nullptr) {
+ add_error_note(ira->codegen, msg, source_instr->source_node,
+ buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i));
+ return ira->codegen->invalid_instruction;
+ }
+ }
+ out_val->type = type_entry;
+ out_val->special = ConstValSpecialStatic;
+ } else {
+ if (ir_eval_math_op_scalar(ira, source_instr, type_entry, op1_val, op_id, op2_val, out_val) != nullptr) {
+ return ira->codegen->invalid_instruction;
+ }
+ }
+ return ir_implicit_cast(ira, result_instruction, type_entry);
}
static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
@@ -12029,24 +12148,7 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
- IrInstruction *result_instruction = ir_const(ira, &bin_op_instruction->base, op1->value.type);
-
- int err;
- if ((err = ir_eval_math_op(op1->value.type, op1_val, op_id, op2_val, &result_instruction->value))) {
- if (err == ErrorOverflow) {
- ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("operation caused overflow"));
- return ira->codegen->invalid_instruction;
- } else if (err == ErrorShiftedOutOneBits) {
- ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("exact shift shifted out 1 bits"));
- return ira->codegen->invalid_instruction;
- } else {
- zig_unreachable();
- }
- return ira->codegen->invalid_instruction;
- }
-
- ir_num_lit_fits_in_other_type(ira, result_instruction, op1->value.type, false);
- return result_instruction;
+ return ir_analyze_math_op(ira, &bin_op_instruction->base, op1->value.type, op1_val, op_id, op2_val);
} else if (op1->value.type->id == ZigTypeIdComptimeInt) {
ir_add_error(ira, &bin_op_instruction->base,
buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known"));
@@ -12292,30 +12394,7 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
- IrInstruction *result_instruction = ir_const(ira, &instruction->base, resolved_type);
-
- int err;
- if ((err = ir_eval_math_op(resolved_type, op1_val, op_id, op2_val, &result_instruction->value))) {
- if (err == ErrorDivByZero) {
- ir_add_error(ira, &instruction->base, buf_sprintf("division by zero"));
- return ira->codegen->invalid_instruction;
- } else if (err == ErrorOverflow) {
- ir_add_error(ira, &instruction->base, buf_sprintf("operation caused overflow"));
- return ira->codegen->invalid_instruction;
- } else if (err == ErrorExactDivRemainder) {
- ir_add_error(ira, &instruction->base, buf_sprintf("exact division had a remainder"));
- return ira->codegen->invalid_instruction;
- } else if (err == ErrorNegativeDenominator) {
- ir_add_error(ira, &instruction->base, buf_sprintf("negative denominator"));
- return ira->codegen->invalid_instruction;
- } else {
- zig_unreachable();
- }
- return ira->codegen->invalid_instruction;
- }
-
- ir_num_lit_fits_in_other_type(ira, result_instruction, resolved_type, false);
- return result_instruction;
+ return ir_analyze_math_op(ira, &instruction->base, resolved_type, op1_val, op_id, op2_val);
}
IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope,
@@ -18745,10 +18824,7 @@ static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstr
if (type_is_invalid(elem_type))
return ira->codegen->invalid_instruction;
- if (elem_type->id != ZigTypeIdInt &&
- elem_type->id != ZigTypeIdFloat &&
- get_codegen_ptr_type(elem_type) == nullptr)
- {
+ if (!is_valid_vector_elem_type(elem_type)) {
ir_add_error(ira, instruction->elem_type,
buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid",
buf_ptr(&elem_type->name)));
@@ -20345,6 +20421,17 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct
return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value);
}
+static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *val, size_t len) {
+ size_t buf_i = 0;
+ // TODO optimize the buf case
+ expand_undef_array(codegen, val);
+ for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) {
+ ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i];
+ buf_write_value_bytes(codegen, &buf[buf_i], elem);
+ buf_i += type_size(codegen, elem->type);
+ }
+}
+
static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) {
if (val->special == ConstValSpecialUndef)
val->special = ConstValSpecialStatic;
@@ -20390,26 +20477,9 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
zig_unreachable();
}
case ZigTypeIdArray:
- {
- size_t buf_i = 0;
- // TODO optimize the buf case
- expand_undef_array(codegen, val);
- for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) {
- ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i];
- buf_write_value_bytes(codegen, &buf[buf_i], elem);
- buf_i += type_size(codegen, elem->type);
- }
- }
- return;
- case ZigTypeIdVector: {
- size_t buf_i = 0;
- for (uint32_t elem_i = 0; elem_i < val->type->data.vector.len; elem_i += 1) {
- ConstExprValue *elem = &val->data.x_vector.elements[elem_i];
- buf_write_value_bytes(codegen, &buf[buf_i], elem);
- buf_i += type_size(codegen, elem->type);
- }
- return;
- }
+ return buf_write_value_bytes_array(codegen, buf, val, val->type->data.array.len);
+ case ZigTypeIdVector:
+ return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len);
case ZigTypeIdStruct:
zig_panic("TODO buf_write_value_bytes struct type");
case ZigTypeIdOptional:
@@ -20426,6 +20496,31 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
zig_unreachable();
}
+static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf,
+ ConstExprValue *val, ZigType *elem_type, size_t len)
+{
+ Error err;
+ uint64_t elem_size = type_size(codegen, elem_type);
+
+ switch (val->data.x_array.special) {
+ case ConstArraySpecialNone:
+ val->data.x_array.data.s_none.elements = create_const_vals(len);
+ for (size_t i = 0; i < len; i++) {
+ ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i];
+ elem->special = ConstValSpecialStatic;
+ elem->type = elem_type;
+ if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
+ return err;
+ }
+ return ErrorNone;
+ case ConstArraySpecialUndef:
+ zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type");
+ case ConstArraySpecialBuf:
+ zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type");
+ }
+ zig_unreachable();
+}
+
static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val) {
Error err;
assert(val->special == ConstValSpecialStatic);
@@ -20464,42 +20559,12 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn);
return ErrorNone;
}
- case ZigTypeIdArray: {
- uint64_t elem_size = type_size(codegen, val->type->data.array.child_type);
- size_t len = val->type->data.array.len;
-
- switch (val->data.x_array.special) {
- case ConstArraySpecialNone:
- val->data.x_array.data.s_none.elements = create_const_vals(len);
- for (size_t i = 0; i < len; i++) {
- ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i];
- elem->special = ConstValSpecialStatic;
- elem->type = val->type->data.array.child_type;
- if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
- return err;
- }
- return ErrorNone;
- case ConstArraySpecialUndef:
- zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type");
- case ConstArraySpecialBuf:
- zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type");
- }
- zig_unreachable();
- }
- case ZigTypeIdVector: {
- uint64_t elem_size = type_size(codegen, val->type->data.vector.elem_type);
- uint32_t len = val->type->data.vector.len;
-
- val->data.x_vector.elements = create_const_vals(len);
- for (uint32_t i = 0; i < len; i += 1) {
- ConstExprValue *elem = &val->data.x_vector.elements[i];
- elem->special = ConstValSpecialStatic;
- elem->type = val->type->data.vector.elem_type;
- if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
- return err;
- }
- return ErrorNone;
- }
+ case ZigTypeIdArray:
+ return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.array.child_type,
+ val->type->data.array.len);
+ case ZigTypeIdVector:
+ return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.vector.elem_type,
+ val->type->data.vector.len);
case ZigTypeIdEnum:
switch (val->type->data.enumeration.layout) {
case ContainerLayoutAuto:
@@ -21634,6 +21699,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
case IrInstructionIdDeclVarGen:
case IrInstructionIdPtrCastGen:
case IrInstructionIdCmpxchgGen:
+ case IrInstructionIdArrayToVector:
+ case IrInstructionIdVectorToArray:
zig_unreachable();
case IrInstructionIdReturn:
@@ -22129,6 +22196,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdFromBytes:
case IrInstructionIdToBytes:
case IrInstructionIdEnumToInt:
+ case IrInstructionIdVectorToArray:
+ case IrInstructionIdArrayToVector:
return false;
case IrInstructionIdAsm:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index a1fd450b65..e19aa6dda8 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -972,6 +972,18 @@ static void ir_print_check_runtime_scope(IrPrint *irp, IrInstructionCheckRuntime
fprintf(irp->f, ")");
}
+static void ir_print_array_to_vector(IrPrint *irp, IrInstructionArrayToVector *instruction) {
+ fprintf(irp->f, "ArrayToVector(");
+ ir_print_other_instruction(irp, instruction->array);
+ fprintf(irp->f, ")");
+}
+
+static void ir_print_vector_to_array(IrPrint *irp, IrInstructionVectorToArray *instruction) {
+ fprintf(irp->f, "VectorToArray(");
+ ir_print_other_instruction(irp, instruction->vector);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) {
fprintf(irp->f, "inttoerr ");
ir_print_other_instruction(irp, instruction->target);
@@ -1825,6 +1837,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdDeclVarGen:
ir_print_decl_var_gen(irp, (IrInstructionDeclVarGen *)instruction);
break;
+ case IrInstructionIdArrayToVector:
+ ir_print_array_to_vector(irp, (IrInstructionArrayToVector *)instruction);
+ break;
+ case IrInstructionIdVectorToArray:
+ ir_print_vector_to_array(irp, (IrInstructionVectorToArray *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig
index e545a4c418..1fa00b34fd 100644
--- a/test/stage1/behavior.zig
+++ b/test/stage1/behavior.zig
@@ -74,6 +74,7 @@ comptime {
_ = @import("behavior/underscore.zig");
_ = @import("behavior/union.zig");
_ = @import("behavior/var_args.zig");
+ _ = @import("behavior/vector.zig");
_ = @import("behavior/void.zig");
_ = @import("behavior/while.zig");
_ = @import("behavior/widening.zig");
diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig
new file mode 100644
index 0000000000..53c5d01381
--- /dev/null
+++ b/test/stage1/behavior/vector.zig
@@ -0,0 +1,20 @@
+const std = @import("std");
+const assertOrPanic = std.debug.assertOrPanic;
+
+test "implicit array to vector and vector to array" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40};
+ const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4};
+ v +%= x;
+ const result: [4]i32 = v;
+ assertOrPanic(result[0] == 11);
+ assertOrPanic(result[1] == 22);
+ assertOrPanic(result[2] == 33);
+ assertOrPanic(result[3] == 44);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
--
cgit v1.2.3
From 4010f6a11dafa1d047d66a637a0efe58d80a52c6 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 5 Feb 2019 11:00:32 +0100
Subject: Added support for vector wrapping mult and sub * I also merged the
code that generates ir for add, sub, and mult
---
src/codegen.cpp | 122 +++++++++++++++++-----------------------
test/stage1/behavior/vector.zig | 17 +++---
2 files changed, 58 insertions(+), 81 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/codegen.cpp b/src/codegen.cpp
index de2222afb7..226a137796 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -2582,6 +2582,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
}
+typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
+
static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
IrInstructionBinOp *bin_op_instruction)
{
@@ -2640,50 +2642,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
} else {
zig_unreachable();
}
+ case IrBinOpMult:
+ case IrBinOpMultWrap:
case IrBinOpAdd:
case IrBinOpAddWrap:
+ case IrBinOpSub:
+ case IrBinOpSubWrap: {
+ // These are lookup table using the AddSubMul enum as the lookup.
+ // If AddSubMul ever changes, then these tables will be out of
+ // date.
+ static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul };
+ static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul };
+ static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
+ static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
+
+ bool is_vector = type_entry->id == ZigTypeIdVector;
+ bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap);
+ AddSubMul add_sub_mul =
+ op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :
+ op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub :
+ AddSubMulMul;
+
+ // The code that is generated for vectors and scalars are the same,
+ // so we can just set type_entry to the vectors elem_type an avoid
+ // a lot of repeated code.
+ if (is_vector)
+ type_entry = type_entry->data.vector.elem_type;
+
if (type_entry->id == ZigTypeIdPointer) {
assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
+ LLVMValueRef subscript_value;
+ if (is_vector)
+ zig_panic("TODO: Implement vector operations on pointers.");
+
+ switch (add_sub_mul) {
+ case AddSubMulAdd:
+ subscript_value = op2_value;
+ break;
+ case AddSubMulSub:
+ subscript_value = LLVMBuildNeg(g->builder, op2_value, "");
+ break;
+ case AddSubMulMul:
+ zig_unreachable();
+ }
+
// TODO runtime safety
- return LLVMBuildInBoundsGEP(g->builder, op1_value, &op2_value, 1, "");
+ return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
} else if (type_entry->id == ZigTypeIdFloat) {
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
- return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
+ return float_op[add_sub_mul](g->builder, op1_value, op2_value, "");
} else if (type_entry->id == ZigTypeIdInt) {
- bool is_wrapping = (op_id == IrBinOpAddWrap);
if (is_wrapping) {
- return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
+ return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, "");
} else if (want_runtime_safety) {
- return gen_overflow_op(g, type_entry, AddSubMulAdd, op1_value, op2_value);
+ if (is_vector)
+ zig_panic("TODO: Implement runtime safety vector operations.");
+ return gen_overflow_op(g, type_entry, add_sub_mul, op1_value, op2_value);
} else if (type_entry->data.integral.is_signed) {
- return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
- } else {
- return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
- }
- } else if (type_entry->id == ZigTypeIdVector) {
- ZigType *elem_type = type_entry->data.vector.elem_type;
- if (elem_type->id == ZigTypeIdFloat) {
- ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
- return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
- } else if (elem_type->id == ZigTypeIdPointer) {
- zig_panic("TODO codegen for pointers in vectors");
- } else if (elem_type->id == ZigTypeIdInt) {
- bool is_wrapping = (op_id == IrBinOpAddWrap);
- if (is_wrapping) {
- return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
- } else if (want_runtime_safety) {
- zig_panic("TODO runtime safety for vector integer addition");
- } else if (elem_type->data.integral.is_signed) {
- return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
- } else {
- return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
- }
+ return signed_op[add_sub_mul](g->builder, op1_value, op2_value, "");
} else {
- zig_unreachable();
+ return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, "");
}
} else {
zig_unreachable();
}
+ }
case IrBinOpBinOr:
return LLVMBuildOr(g->builder, op1_value, op2_value, "");
case IrBinOpBinXor:
@@ -2728,49 +2751,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, "");
}
}
- case IrBinOpSub:
- case IrBinOpSubWrap:
- if (type_entry->id == ZigTypeIdPointer) {
- assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
- // TODO runtime safety
- LLVMValueRef subscript_value = LLVMBuildNeg(g->builder, op2_value, "");
- return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
- } else if (type_entry->id == ZigTypeIdFloat) {
- ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
- return LLVMBuildFSub(g->builder, op1_value, op2_value, "");
- } else if (type_entry->id == ZigTypeIdInt) {
- bool is_wrapping = (op_id == IrBinOpSubWrap);
- if (is_wrapping) {
- return LLVMBuildSub(g->builder, op1_value, op2_value, "");
- } else if (want_runtime_safety) {
- return gen_overflow_op(g, type_entry, AddSubMulSub, op1_value, op2_value);
- } else if (type_entry->data.integral.is_signed) {
- return LLVMBuildNSWSub(g->builder, op1_value, op2_value, "");
- } else {
- return LLVMBuildNUWSub(g->builder, op1_value, op2_value, "");
- }
- } else {
- zig_unreachable();
- }
- case IrBinOpMult:
- case IrBinOpMultWrap:
- if (type_entry->id == ZigTypeIdFloat) {
- ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
- return LLVMBuildFMul(g->builder, op1_value, op2_value, "");
- } else if (type_entry->id == ZigTypeIdInt) {
- bool is_wrapping = (op_id == IrBinOpMultWrap);
- if (is_wrapping) {
- return LLVMBuildMul(g->builder, op1_value, op2_value, "");
- } else if (want_runtime_safety) {
- return gen_overflow_op(g, type_entry, AddSubMulMul, op1_value, op2_value);
- } else if (type_entry->data.integral.is_signed) {
- return LLVMBuildNSWMul(g->builder, op1_value, op2_value, "");
- } else {
- return LLVMBuildNUWMul(g->builder, op1_value, op2_value, "");
- }
- } else {
- zig_unreachable();
- }
case IrBinOpDivUnspecified:
return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
op1_value, op2_value, type_entry, DivKindFloat);
diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig
index 53c5d01381..c97ee0e3d6 100644
--- a/test/stage1/behavior/vector.zig
+++ b/test/stage1/behavior/vector.zig
@@ -1,20 +1,17 @@
const std = @import("std");
+const mem = std.mem;
const assertOrPanic = std.debug.assertOrPanic;
-test "implicit array to vector and vector to array" {
+test "vector wrap operators" {
const S = struct {
fn doTheTest() void {
- var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40};
- const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4};
- v +%= x;
- const result: [4]i32 = v;
- assertOrPanic(result[0] == 11);
- assertOrPanic(result[1] == 22);
- assertOrPanic(result[2] == 33);
- assertOrPanic(result[3] == 44);
+ const v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 };
+ const x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 };
+ assertOrPanic(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ 11, 22, 33, 44 }));
+ assertOrPanic(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 9, 18, 27, 36 }));
+ assertOrPanic(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 10, 40, 90, 160 }));
}
};
S.doTheTest();
comptime S.doTheTest();
}
-
--
cgit v1.2.3
From b1775ca168e0bcfba6753346c5226881da49c6c4 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 6 Feb 2019 13:48:04 -0500
Subject: thread local storage working for linux x86_64
---
CMakeLists.txt | 1 +
src/all_types.hpp | 13 +++--
src/analyze.cpp | 69 +++++++++++++++---------
src/analyze.hpp | 1 +
src/ast_render.cpp | 7 ++-
src/codegen.cpp | 7 +++
src/ir.cpp | 4 ++
src/parser.cpp | 22 +++++---
src/tokenizer.cpp | 2 +
src/tokenizer.hpp | 1 +
std/debug/index.zig | 1 -
std/heap.zig | 7 +--
std/index.zig | 3 +-
std/mem.zig | 20 +++++++
std/os/index.zig | 119 +++++++++++++++++++++++-------------------
std/os/startup.zig | 26 +++++++++
std/os/test.zig | 16 ++++++
std/special/bootstrap.zig | 63 ++++++++++++++++++++--
test/compile_errors.zig | 19 +++++++
test/stage1/behavior/misc.zig | 8 +++
20 files changed, 306 insertions(+), 103 deletions(-)
create mode 100644 std/os/startup.zig
(limited to 'src/codegen.cpp')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4dd6a1dcfa..a093bfbfd9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -587,6 +587,7 @@ set(ZIG_STD_FILES
"os/linux/vdso.zig"
"os/linux/x86_64.zig"
"os/path.zig"
+ "os/startup.zig"
"os/time.zig"
"os/uefi.zig"
"os/windows/advapi32.zig"
diff --git a/src/all_types.hpp b/src/all_types.hpp
index c4c9e13cfb..5af4e71157 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -544,12 +544,7 @@ struct AstNodeDefer {
};
struct AstNodeVariableDeclaration {
- VisibMod visib_mod;
Buf *symbol;
- bool is_const;
- bool is_comptime;
- bool is_export;
- bool is_extern;
// one or both of type and expr will be non null
AstNode *type;
AstNode *expr;
@@ -559,6 +554,13 @@ struct AstNodeVariableDeclaration {
AstNode *align_expr;
// populated if the "section(S)" is present
AstNode *section_expr;
+ Token *threadlocal_tok;
+
+ VisibMod visib_mod;
+ bool is_const;
+ bool is_comptime;
+ bool is_export;
+ bool is_extern;
};
struct AstNodeTestDecl {
@@ -1873,6 +1875,7 @@ struct ZigVar {
bool shadowable;
bool src_is_const;
bool gen_is_const;
+ bool is_thread_local;
};
struct ErrorTableEntry {
diff --git a/src/analyze.cpp b/src/analyze.cpp
index ff961a7044..96f1faaaa9 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum
static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);
static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
-ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
- if (node->owner->c_import_node != nullptr) {
- // if this happens, then translate_c generated code that
- // failed semantic analysis, which isn't supposed to happen
- ErrorMsg *err = add_node_error(g, node->owner->c_import_node,
- buf_sprintf("compiler bug: @cImport generated invalid zig code"));
-
- add_error_note(g, err, node, msg);
-
- g->errors.append(err);
- return err;
- }
-
- ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
- node->owner->source_code, node->owner->line_offsets, msg);
-
- g->errors.append(err);
- return err;
-}
-
-ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
- if (node->owner->c_import_node != nullptr) {
+static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ImportTableEntry *owner, Token *token,
+ Buf *msg)
+{
+ if (owner->c_import_node != nullptr) {
// if this happens, then translate_c generated code that
// failed semantic analysis, which isn't supposed to happen
@@ -64,13 +46,46 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m
return note;
}
- ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
- node->owner->source_code, node->owner->line_offsets, msg);
+ ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column,
+ owner->source_code, owner->line_offsets, msg);
err_msg_add_note(parent_msg, err);
return err;
}
+ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg) {
+ if (owner->c_import_node != nullptr) {
+ // if this happens, then translate_c generated code that
+ // failed semantic analysis, which isn't supposed to happen
+ ErrorMsg *err = add_node_error(g, owner->c_import_node,
+ buf_sprintf("compiler bug: @cImport generated invalid zig code"));
+
+ add_error_note_token(g, err, owner, token, msg);
+
+ g->errors.append(err);
+ return err;
+ }
+ ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column,
+ owner->source_code, owner->line_offsets, msg);
+
+ g->errors.append(err);
+ return err;
+}
+
+ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
+ Token fake_token;
+ fake_token.start_line = node->line;
+ fake_token.start_column = node->column;
+ return add_token_error(g, node->owner, &fake_token, msg);
+}
+
+ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
+ Token fake_token;
+ fake_token.start_line = node->line;
+ fake_token.start_column = node->column;
+ return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg);
+}
+
ZigType *new_type_table_entry(ZigTypeId id) {
ZigType *entry = allocate(1);
entry->id = id;
@@ -3668,6 +3683,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
bool is_const = var_decl->is_const;
bool is_extern = var_decl->is_extern;
bool is_export = var_decl->is_export;
+ bool is_thread_local = var_decl->threadlocal_tok != nullptr;
ZigType *explicit_type = nullptr;
if (var_decl->type) {
@@ -3727,6 +3743,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
is_const, init_val, &tld_var->base, type);
tld_var->var->linkage = linkage;
+ tld_var->var->is_thread_local = is_thread_local;
if (implicit_type != nullptr && type_is_invalid(implicit_type)) {
tld_var->var->var_type = g->builtin_types.entry_invalid;
@@ -3747,6 +3764,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
}
}
+ if (is_thread_local && is_const) {
+ add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant"));
+ }
+
g->global_vars.append(tld_var);
}
diff --git a/src/analyze.hpp b/src/analyze.hpp
index f558fa44b0..9773782510 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -12,6 +12,7 @@
void semantic_analyze(CodeGen *g);
ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
+ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg);
ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg);
ZigType *new_type_table_entry(ZigTypeId id);
ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
index 994ba5f5b1..34a7faa2a5 100644
--- a/src/ast_render.cpp
+++ b/src/ast_render.cpp
@@ -132,6 +132,10 @@ static const char *const_or_var_string(bool is_const) {
return is_const ? "const" : "var";
}
+static const char *thread_local_string(Token *tok) {
+ return (tok == nullptr) ? "" : "threadlocal ";
+}
+
const char *container_string(ContainerKind kind) {
switch (kind) {
case ContainerKindEnum: return "enum";
@@ -554,8 +558,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
{
const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);
const char *extern_str = extern_string(node->data.variable_declaration.is_extern);
+ const char *thread_local_str = thread_local_string(node->data.variable_declaration.threadlocal_tok);
const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const);
- fprintf(ar->f, "%s%s%s ", pub_str, extern_str, const_or_var);
+ fprintf(ar->f, "%s%s%s%s ", pub_str, extern_str, thread_local_str, const_or_var);
print_symbol(ar, node->data.variable_declaration.symbol);
if (node->data.variable_declaration.type) {
diff --git a/src/codegen.cpp b/src/codegen.cpp
index de2222afb7..d8fc077efc 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -6445,6 +6445,9 @@ static void do_code_gen(CodeGen *g) {
maybe_import_dll(g, global_value, GlobalLinkageIdStrong);
LLVMSetAlignment(global_value, var->align_bytes);
LLVMSetGlobalConstant(global_value, var->gen_is_const);
+ if (var->is_thread_local && !g->is_single_threaded) {
+ LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
+ }
}
} else {
bool exported = (var->linkage == VarLinkageExport);
@@ -6470,6 +6473,9 @@ static void do_code_gen(CodeGen *g) {
}
LLVMSetGlobalConstant(global_value, var->gen_is_const);
+ if (var->is_thread_local && !g->is_single_threaded) {
+ LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
+ }
}
var->value_ref = global_value;
@@ -7520,6 +7526,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename);
g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
+ g->std_package->package_table.put(buf_create_from_str("std"), g->std_package);
g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents);
scan_import(g, g->compile_var_import);
diff --git a/src/ir.cpp b/src/ir.cpp
index 3cbbdc8103..02b2b12230 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -5204,6 +5204,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
add_node_error(irb->codegen, variable_declaration->section_expr,
buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
}
+ if (variable_declaration->threadlocal_tok != nullptr) {
+ add_token_error(irb->codegen, node->owner, variable_declaration->threadlocal_tok,
+ buf_sprintf("function-local variable '%s' cannot be threadlocal", buf_ptr(variable_declaration->symbol)));
+ }
// Temporarily set the name of the IrExecutable to the VariableDeclaration
// so that the struct or enum from the init expression inherits the name.
diff --git a/src/parser.cpp b/src/parser.cpp
index 81bd469d1c..1c1af87c51 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
static AstNode *ast_parse_var_decl(ParseContext *pc) {
- Token *first = eat_token_if(pc, TokenIdKeywordConst);
- if (first == nullptr)
- first = eat_token_if(pc, TokenIdKeywordVar);
- if (first == nullptr)
- return nullptr;
-
+ Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
+ Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst);
+ if (mut_kw == nullptr)
+ mut_kw = eat_token_if(pc, TokenIdKeywordVar);
+ if (mut_kw == nullptr) {
+ if (thread_local_kw == nullptr) {
+ return nullptr;
+ } else {
+ ast_invalid_token_error(pc, peek_token(pc));
+ }
+ }
Token *identifier = expect_token(pc, TokenIdSymbol);
AstNode *type_expr = nullptr;
if (eat_token_if(pc, TokenIdColon) != nullptr)
@@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) {
expect_token(pc, TokenIdSemicolon);
- AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, first);
- res->data.variable_declaration.is_const = first->id == TokenIdKeywordConst;
+ AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw);
+ res->data.variable_declaration.threadlocal_tok = thread_local_kw;
+ res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst;
res->data.variable_declaration.symbol = token_buf(identifier);
res->data.variable_declaration.type = type_expr;
res->data.variable_declaration.align_expr = align_expr;
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index d43bfabf6d..3acd605748 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = {
{"suspend", TokenIdKeywordSuspend},
{"switch", TokenIdKeywordSwitch},
{"test", TokenIdKeywordTest},
+ {"threadlocal", TokenIdKeywordThreadLocal},
{"true", TokenIdKeywordTrue},
{"try", TokenIdKeywordTry},
{"undefined", TokenIdKeywordUndefined},
@@ -1586,6 +1587,7 @@ const char * token_name(TokenId id) {
case TokenIdKeywordStruct: return "struct";
case TokenIdKeywordSwitch: return "switch";
case TokenIdKeywordTest: return "test";
+ case TokenIdKeywordThreadLocal: return "threadlocal";
case TokenIdKeywordTrue: return "true";
case TokenIdKeywordTry: return "try";
case TokenIdKeywordUndefined: return "undefined";
diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp
index 1574e95571..17f36699b3 100644
--- a/src/tokenizer.hpp
+++ b/src/tokenizer.hpp
@@ -88,6 +88,7 @@ enum TokenId {
TokenIdKeywordSuspend,
TokenIdKeywordSwitch,
TokenIdKeywordTest,
+ TokenIdKeywordThreadLocal,
TokenIdKeywordTrue,
TokenIdKeywordTry,
TokenIdKeywordUndefined,
diff --git a/std/debug/index.zig b/std/debug/index.zig
index 838bd0c166..b4ef849509 100644
--- a/std/debug/index.zig
+++ b/std/debug/index.zig
@@ -37,7 +37,6 @@ const Module = struct {
var stderr_file: os.File = undefined;
var stderr_file_out_stream: os.File.OutStream = undefined;
-/// TODO multithreaded awareness
var stderr_stream: ?*io.OutStream(os.File.WriteError) = null;
var stderr_mutex = std.Mutex.init();
pub fn warn(comptime fmt: []const u8, args: ...) void {
diff --git a/std/heap.zig b/std/heap.zig
index fd2ce1e965..1403f8e831 100644
--- a/std/heap.zig
+++ b/std/heap.zig
@@ -106,9 +106,7 @@ pub const DirectAllocator = struct {
};
const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
const root_addr = @ptrToInt(ptr);
- const rem = @rem(root_addr, alignment);
- const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
- const adjusted_addr = root_addr + march_forward_bytes;
+ const adjusted_addr = mem.alignForward(root_addr, alignment);
const record_addr = adjusted_addr + n;
@intToPtr(*align(1) usize, record_addr).* = root_addr;
return @intToPtr([*]u8, adjusted_addr)[0..n];
@@ -126,8 +124,7 @@ pub const DirectAllocator = struct {
const base_addr = @ptrToInt(old_mem.ptr);
const old_addr_end = base_addr + old_mem.len;
const new_addr_end = base_addr + new_size;
- const rem = @rem(new_addr_end, os.page_size);
- const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
+ const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
if (old_addr_end > new_addr_end_rounded) {
_ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
}
diff --git a/std/index.zig b/std/index.zig
index 80d1e46bb6..ef3988460f 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -33,8 +33,8 @@ pub const io = @import("io.zig");
pub const json = @import("json.zig");
pub const macho = @import("macho.zig");
pub const math = @import("math/index.zig");
-pub const meta = @import("meta/index.zig");
pub const mem = @import("mem.zig");
+pub const meta = @import("meta/index.zig");
pub const net = @import("net.zig");
pub const os = @import("os/index.zig");
pub const pdb = @import("pdb.zig");
@@ -45,6 +45,7 @@ pub const unicode = @import("unicode.zig");
pub const zig = @import("zig/index.zig");
pub const lazyInit = @import("lazy_init.zig").lazyInit;
+pub const startup = @import("os/startup.zig");
test "std" {
// run tests from these
diff --git a/std/mem.zig b/std/mem.zig
index 26ae4ef089..178a5f6c6f 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -1366,3 +1366,23 @@ test "std.mem.subArrayPtr" {
sub2[1] = 'X';
debug.assert(std.mem.eql(u8, a2, "abcXef"));
}
+
+/// Round an address up to the nearest aligned address
+pub fn alignForward(addr: usize, alignment: usize) usize {
+ return (addr + alignment - 1) & ~(alignment - 1);
+}
+
+test "std.mem.alignForward" {
+ debug.assertOrPanic(alignForward(1, 1) == 1);
+ debug.assertOrPanic(alignForward(2, 1) == 2);
+ debug.assertOrPanic(alignForward(1, 2) == 2);
+ debug.assertOrPanic(alignForward(2, 2) == 2);
+ debug.assertOrPanic(alignForward(3, 2) == 4);
+ debug.assertOrPanic(alignForward(4, 2) == 4);
+ debug.assertOrPanic(alignForward(7, 8) == 8);
+ debug.assertOrPanic(alignForward(8, 8) == 8);
+ debug.assertOrPanic(alignForward(9, 8) == 16);
+ debug.assertOrPanic(alignForward(15, 8) == 16);
+ debug.assertOrPanic(alignForward(16, 8) == 16);
+ debug.assertOrPanic(alignForward(17, 8) == 24);
+}
diff --git a/std/os/index.zig b/std/os/index.zig
index 451c0a3436..68b3409757 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -8,6 +8,9 @@ const is_posix = switch (builtin.os) {
};
const os = @This();
+// See the comment in startup.zig for why this does not use the `std` global above.
+const startup = @import("std").startup;
+
test "std.os" {
_ = @import("child_process.zig");
_ = @import("darwin.zig");
@@ -667,14 +670,11 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
}
}
-pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null;
-pub var posix_environ_raw: [][*]u8 = undefined;
-
/// See std.elf for the constants.
pub fn linuxGetAuxVal(index: usize) usize {
if (builtin.link_libc) {
return usize(std.c.getauxval(index));
- } else if (linux_elf_aux_maybe) |auxv| {
+ } else if (startup.linux_elf_aux_maybe) |auxv| {
var i: usize = 0;
while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) {
if (auxv[i].a_type == index)
@@ -692,12 +692,7 @@ pub fn getBaseAddress() usize {
return base;
}
const phdr = linuxGetAuxVal(std.elf.AT_PHDR);
- const ElfHeader = switch (@sizeOf(usize)) {
- 4 => std.elf.Elf32_Ehdr,
- 8 => std.elf.Elf64_Ehdr,
- else => @compileError("Unsupported architecture"),
- };
- return phdr - @sizeOf(ElfHeader);
+ return phdr - @sizeOf(std.elf.Ehdr);
},
builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header),
builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
@@ -739,7 +734,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
try result.setMove(key, value);
}
} else {
- for (posix_environ_raw) |ptr| {
+ for (startup.posix_environ_raw) |ptr| {
var line_i: usize = 0;
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
const key = ptr[0..line_i];
@@ -761,7 +756,7 @@ test "os.getEnvMap" {
/// TODO make this go through libc when we have it
pub fn getEnvPosix(key: []const u8) ?[]const u8 {
- for (posix_environ_raw) |ptr| {
+ for (startup.posix_environ_raw) |ptr| {
var line_i: usize = 0;
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
const this_key = ptr[0..line_i];
@@ -1942,14 +1937,14 @@ pub const ArgIteratorPosix = struct {
pub fn init() ArgIteratorPosix {
return ArgIteratorPosix{
.index = 0,
- .count = raw.len,
+ .count = startup.posix_argv_raw.len,
};
}
pub fn next(self: *ArgIteratorPosix) ?[]const u8 {
if (self.index == self.count) return null;
- const s = raw[self.index];
+ const s = startup.posix_argv_raw[self.index];
self.index += 1;
return cstr.toSlice(s);
}
@@ -1960,10 +1955,6 @@ pub const ArgIteratorPosix = struct {
self.index += 1;
return true;
}
-
- /// This is marked as public but actually it's only meant to be used
- /// internally by zig's startup code.
- pub var raw: [][*]u8 = undefined;
};
pub const ArgIteratorWindows = struct {
@@ -2908,14 +2899,15 @@ pub const Thread = struct {
pub const Data = if (use_pthreads)
struct {
handle: Thread.Handle,
- stack_addr: usize,
- stack_len: usize,
+ mmap_addr: usize,
+ mmap_len: usize,
}
else switch (builtin.os) {
builtin.Os.linux => struct {
handle: Thread.Handle,
- stack_addr: usize,
- stack_len: usize,
+ mmap_addr: usize,
+ mmap_len: usize,
+ tls_end_addr: usize,
},
builtin.Os.windows => struct {
handle: Thread.Handle,
@@ -2955,7 +2947,7 @@ pub const Thread = struct {
posix.EDEADLK => unreachable,
else => unreachable,
}
- assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
+ assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
} else switch (builtin.os) {
builtin.Os.linux => {
while (true) {
@@ -2969,7 +2961,7 @@ pub const Thread = struct {
else => unreachable,
}
}
- assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
+ assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
},
builtin.Os.windows => {
assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
@@ -3097,42 +3089,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
- const mmap_len = default_stack_size;
- const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
- if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
- errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);
+ var stack_end_offset: usize = undefined;
+ var thread_start_offset: usize = undefined;
+ var context_start_offset: usize = undefined;
+ var tls_start_offset: usize = undefined;
+ const mmap_len = blk: {
+ // First in memory will be the stack, which grows downwards.
+ var l: usize = mem.alignForward(default_stack_size, os.page_size);
+ stack_end_offset = l;
+ // Above the stack, so that it can be in the same mmap call, put the Thread object.
+ l = mem.alignForward(l, @alignOf(Thread));
+ thread_start_offset = l;
+ l += @sizeOf(Thread);
+ // Next, the Context object.
+ if (@sizeOf(Context) != 0) {
+ l = mem.alignForward(l, @alignOf(Context));
+ context_start_offset = l;
+ l += @sizeOf(Context);
+ }
+ // Finally, the Thread Local Storage, if any.
+ if (!Thread.use_pthreads) {
+ if (startup.linux_tls_phdr) |tls_phdr| {
+ l = mem.alignForward(l, tls_phdr.p_align);
+ tls_start_offset = l;
+ l += tls_phdr.p_memsz;
+ }
+ }
+ break :blk l;
+ };
+ const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
+ if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory;
+ errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0);
+
+ const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset));
+ thread_ptr.data.mmap_addr = mmap_addr;
+ thread_ptr.data.mmap_len = mmap_len;
- var stack_end: usize = stack_addr + mmap_len;
var arg: usize = undefined;
if (@sizeOf(Context) != 0) {
- stack_end -= @sizeOf(Context);
- stack_end -= stack_end % @alignOf(Context);
- assert(stack_end >= stack_addr);
- const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end));
+ arg = mmap_addr + context_start_offset;
+ const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg));
context_ptr.* = context;
- arg = stack_end;
}
- stack_end -= @sizeOf(Thread);
- stack_end -= stack_end % @alignOf(Thread);
- assert(stack_end >= stack_addr);
- const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, stack_end));
-
- thread_ptr.data.stack_addr = stack_addr;
- thread_ptr.data.stack_len = mmap_len;
-
- if (builtin.os == builtin.Os.windows) {
- // use windows API directly
- @compileError("TODO support spawnThread for Windows");
- } else if (Thread.use_pthreads) {
+ if (Thread.use_pthreads) {
// use pthreads
var attr: c.pthread_attr_t = undefined;
if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
defer assert(c.pthread_attr_destroy(&attr) == 0);
- // align to page
- stack_end -= stack_end % os.page_size;
- assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0);
+ assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0);
const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
switch (err) {
@@ -3143,10 +3149,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
else => return unexpectedErrorPosix(@intCast(usize, err)),
}
} else if (builtin.os == builtin.Os.linux) {
- // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
- const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
- const newtls: usize = 0;
- const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
+ var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND |
+ posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
+ posix.CLONE_DETACHED;
+ var newtls: usize = undefined;
+ if (startup.linux_tls_phdr) |tls_phdr| {
+ @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), startup.linux_tls_img_src, tls_phdr.p_filesz);
+ thread_ptr.data.tls_end_addr = mmap_addr + mmap_len;
+ newtls = @ptrToInt(&thread_ptr.data.tls_end_addr);
+ flags |= posix.CLONE_SETTLS;
+ }
+ const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
const err = posix.getErrno(rc);
switch (err) {
0 => return thread_ptr,
diff --git a/std/os/startup.zig b/std/os/startup.zig
new file mode 100644
index 0000000000..c54d274c5d
--- /dev/null
+++ b/std/os/startup.zig
@@ -0,0 +1,26 @@
+// This file contains global variables that are initialized on startup from
+// std/special/bootstrap.zig. There are a few things to be aware of here.
+//
+// First, when building an object or library, and no entry point is defined
+// (such as pub fn main), std/special/bootstrap.zig is not included in the
+// compilation. And so these global variables will remain set to the values
+// you see here.
+//
+// Second, when using `zig test` to test the standard library, note that
+// `zig test` is self-hosted. This means that it uses std/special/bootstrap.zig
+// and an @import("std") from the install directory, which is distinct from
+// the standard library files that we are directly testing with `zig test`.
+// This means that these global variables would not get set. So the workaround
+// here is that references to these globals from the standard library must
+// use `@import("std").startup` rather than
+// `@import("path/to/std/index.zig").startup` (and rather than the file path of
+// this file directly). We also put "std" as a reference to itself in the
+// standard library package so that this can work.
+
+const std = @import("../index.zig");
+
+pub var linux_tls_phdr: ?*std.elf.Phdr = null;
+pub var linux_tls_img_src: [*]const u8 = undefined; // defined when linux_tls_phdr is non-null
+pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null;
+pub var posix_environ_raw: [][*]u8 = undefined;
+pub var posix_argv_raw: [][*]u8 = undefined;
diff --git a/std/os/test.zig b/std/os/test.zig
index f14cf47786..bd9148d1b1 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -105,3 +105,19 @@ test "AtomicFile" {
try os.deleteFile(test_out_file);
}
+
+test "thread local storage" {
+ if (builtin.single_threaded) return error.SkipZigTest;
+ const thread1 = try std.os.spawnThread({}, testTls);
+ const thread2 = try std.os.spawnThread({}, testTls);
+ testTls({});
+ thread1.wait();
+ thread2.wait();
+}
+
+threadlocal var x: i32 = 1234;
+fn testTls(context: void) void {
+ if (x != 1234) @panic("bad start value");
+ x += 1;
+ if (x != 1235) @panic("bad end value");
+}
diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig
index 129bde913f..0e84f67481 100644
--- a/std/special/bootstrap.zig
+++ b/std/special/bootstrap.zig
@@ -4,6 +4,7 @@
const root = @import("@root");
const std = @import("std");
const builtin = @import("builtin");
+const assert = std.debug.assert;
var argc_ptr: [*]usize = undefined;
@@ -61,9 +62,23 @@ fn posixCallMainAndExit() noreturn {
while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];
if (builtin.os == builtin.Os.linux) {
- const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1);
- std.os.linux_elf_aux_maybe = @ptrCast([*]std.elf.Auxv, auxv);
- std.debug.assert(std.os.linuxGetAuxVal(std.elf.AT_PAGESZ) == std.os.page_size);
+ // Scan auxiliary vector.
+ const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
+ std.startup.linux_elf_aux_maybe = auxv;
+ var i: usize = 0;
+ var at_phdr: usize = 0;
+ var at_phnum: usize = 0;
+ var at_phent: usize = 0;
+ while (auxv[i].a_un.a_val != 0) : (i += 1) {
+ switch (auxv[i].a_type) {
+ std.elf.AT_PAGESZ => assert(auxv[i].a_un.a_val == std.os.page_size),
+ std.elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val,
+ std.elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val,
+ std.elf.AT_PHENT => at_phent = auxv[i].a_un.a_val,
+ else => {},
+ }
+ }
+ if (!builtin.single_threaded) linuxInitializeThreadLocalStorage(at_phdr, at_phnum, at_phent);
}
std.os.posix.exit(callMainWithArgs(argc, argv, envp));
@@ -72,8 +87,8 @@ fn posixCallMainAndExit() noreturn {
// This is marked inline because for some reason LLVM in release mode fails to inline it,
// and we want fewer call frames in stack traces.
inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {
- std.os.ArgIteratorPosix.raw = argv[0..argc];
- std.os.posix_environ_raw = envp;
+ std.startup.posix_argv_raw = argv[0..argc];
+ std.startup.posix_environ_raw = envp;
return callMain();
}
@@ -116,3 +131,41 @@ inline fn callMain() u8 {
else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),
}
}
+
+var tls_end_addr: usize = undefined;
+const main_thread_tls_align = 32;
+var main_thread_tls_bytes: [64]u8 align(main_thread_tls_align) = [1]u8{0} ** 64;
+
+fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: usize) void {
+ var phdr_addr = at_phdr;
+ var n = at_phnum;
+ var base: usize = 0;
+ while (n != 0) : ({n -= 1; phdr_addr += at_phent;}) {
+ const phdr = @intToPtr(*std.elf.Phdr, phdr_addr);
+ // TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917
+ switch (phdr.p_type) {
+ std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr,
+ std.elf.PT_TLS => std.startup.linux_tls_phdr = phdr,
+ else => continue,
+ }
+ }
+ const tls_phdr = std.startup.linux_tls_phdr orelse return;
+ std.startup.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr);
+ assert(main_thread_tls_bytes.len >= tls_phdr.p_memsz); // not enough preallocated Thread Local Storage
+ assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough
+ @memcpy(&main_thread_tls_bytes, std.startup.linux_tls_img_src, tls_phdr.p_filesz);
+ tls_end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz;
+ linuxSetThreadArea(@ptrToInt(&tls_end_addr));
+}
+
+fn linuxSetThreadArea(addr: usize) void {
+ switch (builtin.arch) {
+ builtin.Arch.x86_64 => {
+ const ARCH_SET_FS = 0x1002;
+ const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, ARCH_SET_FS, addr);
+ // acrh_prctl is documented to never fail
+ assert(rc == 0);
+ },
+ else => @compileError("Unsupported architecture"),
+ }
+}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 30d9ca5d70..acd1eada06 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,25 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.add(
+ "threadlocal qualifier on const",
+ \\threadlocal const x: i32 = 1234;
+ \\export fn entry() i32 {
+ \\ return x;
+ \\}
+ ,
+ ".tmp_source.zig:1:13: error: threadlocal variable cannot be constant",
+ );
+
+ cases.add(
+ "threadlocal qualifier on local variable",
+ \\export fn entry() void {
+ \\ threadlocal var x: i32 = 1234;
+ \\}
+ ,
+ ".tmp_source.zig:2:5: error: function-local variable 'x' cannot be threadlocal",
+ );
+
cases.add(
"@bitCast same size but bit count mismatch",
\\export fn entry(byte: u8) void {
diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig
index 8d2555dddd..3cc8e5f31e 100644
--- a/test/stage1/behavior/misc.zig
+++ b/test/stage1/behavior/misc.zig
@@ -685,3 +685,11 @@ test "fn call returning scalar optional in equality expression" {
fn getNull() ?*i32 {
return null;
}
+
+test "thread local variable" {
+ const S = struct {
+ threadlocal var t: i32 = 1234;
+ };
+ S.t += 1;
+ assertOrPanic(S.t == 1235);
+}
--
cgit v1.2.3
From d2602b442e7cdea2e74584f9529917d7a53625fd Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 6 Feb 2019 14:32:20 -0500
Subject: require running std lib tests coherently
this should actually improve CI times a bit too
See the description at the top of std/os/startup.zig (deleted in this
commit) for a more detailed understanding of what this commit does.
---
CMakeLists.txt | 1 -
src/codegen.cpp | 18 +++++++++++++-----
src/codegen.hpp | 2 +-
src/link.cpp | 2 +-
src/main.cpp | 12 +++++++++---
std/build.zig | 10 ++++++++++
std/index.zig | 1 -
std/os/index.zig | 31 +++++++++++++++++++++----------
std/os/startup.zig | 26 --------------------------
std/special/bootstrap.zig | 14 +++++++-------
test/tests.zig | 3 +++
11 files changed, 65 insertions(+), 55 deletions(-)
delete mode 100644 std/os/startup.zig
(limited to 'src/codegen.cpp')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a093bfbfd9..4dd6a1dcfa 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -587,7 +587,6 @@ set(ZIG_STD_FILES
"os/linux/vdso.zig"
"os/linux/x86_64.zig"
"os/path.zig"
- "os/startup.zig"
"os/time.zig"
"os/uefi.zig"
"os/windows/advapi32.zig"
diff --git a/src/codegen.cpp b/src/codegen.cpp
index d8fc077efc..5e282160d6 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -88,7 +88,7 @@ static const char *symbols_that_llvm_depends_on[] = {
};
CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,
- Buf *zig_lib_dir)
+ Buf *zig_lib_dir, Buf *override_std_dir)
{
CodeGen *g = allocate(1);
@@ -96,8 +96,12 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
g->zig_lib_dir = zig_lib_dir;
- g->zig_std_dir = buf_alloc();
- os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);
+ if (override_std_dir == nullptr) {
+ g->zig_std_dir = buf_alloc();
+ os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);
+ } else {
+ g->zig_std_dir = override_std_dir;
+ }
g->zig_c_headers_dir = buf_alloc();
os_path_join(zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir);
@@ -8356,8 +8360,12 @@ static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) {
if (!entry)
break;
- cache_buf(ch, entry->key);
- add_cache_pkg(g, ch, entry->value);
+ // TODO: I think we need a more sophisticated detection of
+ // packages we have already seen
+ if (entry->value != pkg) {
+ cache_buf(ch, entry->key);
+ add_cache_pkg(g, ch, entry->value);
+ }
}
}
diff --git a/src/codegen.hpp b/src/codegen.hpp
index 6f1cdfb677..4bd8f2dcca 100644
--- a/src/codegen.hpp
+++ b/src/codegen.hpp
@@ -15,7 +15,7 @@
#include
CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,
- Buf *zig_lib_dir);
+ Buf *zig_lib_dir, Buf *override_std_dir);
void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);
void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len);
diff --git a/src/link.cpp b/src/link.cpp
index 593f7f309f..58221a99ea 100644
--- a/src/link.cpp
+++ b/src/link.cpp
@@ -42,7 +42,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path)
}
CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type,
- parent_gen->build_mode, parent_gen->zig_lib_dir);
+ parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir);
child_gen->out_h_path = nullptr;
child_gen->verbose_tokenize = parent_gen->verbose_tokenize;
diff --git a/src/main.cpp b/src/main.cpp
index 81f49089be..73a1d75d42 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -74,6 +74,7 @@ static int print_full_usage(const char *arg0) {
" -dirafter [dir] same as -isystem but do it last\n"
" -isystem [dir] add additional search path for other .h files\n"
" -mllvm [arg] forward an arg to LLVM's option processing\n"
+ " --override-std-dir [arg] use an alternate Zig standard library\n"
"\n"
"Link Options:\n"
" --dynamic-linker [path] set the path to ld.so\n"
@@ -395,6 +396,7 @@ int main(int argc, char **argv) {
bool system_linker_hack = false;
TargetSubsystem subsystem = TargetSubsystemAuto;
bool is_single_threaded = false;
+ Buf *override_std_dir = nullptr;
if (argc >= 2 && strcmp(argv[1], "build") == 0) {
Buf zig_exe_path_buf = BUF_INIT;
@@ -430,7 +432,8 @@ int main(int argc, char **argv) {
Buf *build_runner_path = buf_alloc();
os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path);
- CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir());
+ CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(),
+ override_std_dir);
g->enable_time_report = timing_info;
buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);
codegen_set_out_name(g, buf_create_from_str("build"));
@@ -645,6 +648,8 @@ int main(int argc, char **argv) {
clang_argv.append(argv[i]);
llvm_argv.append(argv[i]);
+ } else if (strcmp(arg, "--override-std-dir") == 0) {
+ override_std_dir = buf_create_from_str(argv[i]);
} else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {
lib_dirs.append(argv[i]);
} else if (strcmp(arg, "--library") == 0) {
@@ -819,7 +824,7 @@ int main(int argc, char **argv) {
switch (cmd) {
case CmdBuiltin: {
- CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir());
+ CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir);
g->is_single_threaded = is_single_threaded;
Buf *builtin_source = codegen_generate_builtin_source(g);
if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
@@ -878,7 +883,8 @@ int main(int argc, char **argv) {
if (cmd == CmdRun && buf_out_name == nullptr) {
buf_out_name = buf_create_from_str("run");
}
- CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir());
+ CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(),
+ override_std_dir);
g->subsystem = subsystem;
if (disable_pic) {
diff --git a/std/build.zig b/std/build.zig
index 5246d97339..07efcec30d 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -1686,6 +1686,7 @@ pub const TestStep = struct {
no_rosegment: bool,
output_path: ?[]const u8,
system_linker_hack: bool,
+ override_std_dir: ?[]const u8,
pub fn init(builder: *Builder, root_src: []const u8) TestStep {
const step_name = builder.fmt("test {}", root_src);
@@ -1707,6 +1708,7 @@ pub const TestStep = struct {
.no_rosegment = false,
.output_path = null,
.system_linker_hack = false,
+ .override_std_dir = null,
};
}
@@ -1737,6 +1739,10 @@ pub const TestStep = struct {
self.build_mode = mode;
}
+ pub fn overrideStdDir(self: *TestStep, dir_path: []const u8) void {
+ self.override_std_dir = dir_path;
+ }
+
pub fn setOutputPath(self: *TestStep, file_path: []const u8) void {
self.output_path = file_path;
@@ -1914,6 +1920,10 @@ pub const TestStep = struct {
if (self.system_linker_hack) {
try zig_args.append("--system-linker-hack");
}
+ if (self.override_std_dir) |dir| {
+ try zig_args.append("--override-std-dir");
+ try zig_args.append(builder.pathFromRoot(dir));
+ }
try builder.spawnChild(zig_args.toSliceConst());
}
diff --git a/std/index.zig b/std/index.zig
index ef3988460f..2a63244004 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -45,7 +45,6 @@ pub const unicode = @import("unicode.zig");
pub const zig = @import("zig/index.zig");
pub const lazyInit = @import("lazy_init.zig").lazyInit;
-pub const startup = @import("os/startup.zig");
test "std" {
// run tests from these
diff --git a/std/os/index.zig b/std/os/index.zig
index 68b3409757..d17b6f4f40 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -8,8 +8,9 @@ const is_posix = switch (builtin.os) {
};
const os = @This();
-// See the comment in startup.zig for why this does not use the `std` global above.
-const startup = @import("std").startup;
+comptime {
+ assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir
+}
test "std.os" {
_ = @import("child_process.zig");
@@ -670,11 +671,14 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
}
}
+pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null;
+pub var posix_environ_raw: [][*]u8 = undefined;
+
/// See std.elf for the constants.
pub fn linuxGetAuxVal(index: usize) usize {
if (builtin.link_libc) {
return usize(std.c.getauxval(index));
- } else if (startup.linux_elf_aux_maybe) |auxv| {
+ } else if (linux_elf_aux_maybe) |auxv| {
var i: usize = 0;
while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) {
if (auxv[i].a_type == index)
@@ -734,7 +738,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
try result.setMove(key, value);
}
} else {
- for (startup.posix_environ_raw) |ptr| {
+ for (posix_environ_raw) |ptr| {
var line_i: usize = 0;
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
const key = ptr[0..line_i];
@@ -756,7 +760,7 @@ test "os.getEnvMap" {
/// TODO make this go through libc when we have it
pub fn getEnvPosix(key: []const u8) ?[]const u8 {
- for (startup.posix_environ_raw) |ptr| {
+ for (posix_environ_raw) |ptr| {
var line_i: usize = 0;
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
const this_key = ptr[0..line_i];
@@ -1937,14 +1941,14 @@ pub const ArgIteratorPosix = struct {
pub fn init() ArgIteratorPosix {
return ArgIteratorPosix{
.index = 0,
- .count = startup.posix_argv_raw.len,
+ .count = raw.len,
};
}
pub fn next(self: *ArgIteratorPosix) ?[]const u8 {
if (self.index == self.count) return null;
- const s = startup.posix_argv_raw[self.index];
+ const s = raw[self.index];
self.index += 1;
return cstr.toSlice(s);
}
@@ -1955,6 +1959,10 @@ pub const ArgIteratorPosix = struct {
self.index += 1;
return true;
}
+
+ /// This is marked as public but actually it's only meant to be used
+ /// internally by zig's startup code.
+ pub var raw: [][*]u8 = undefined;
};
pub const ArgIteratorWindows = struct {
@@ -3000,6 +3008,9 @@ pub const SpawnThreadError = error{
Unexpected,
};
+pub var linux_tls_phdr: ?*std.elf.Phdr = null;
+pub var linux_tls_img_src: [*]const u8 = undefined; // defined if linux_tls_phdr is
+
/// caller must call wait on the returned thread
/// fn startFn(@typeOf(context)) T
/// where T is u8, noreturn, void, or !void
@@ -3109,7 +3120,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
}
// Finally, the Thread Local Storage, if any.
if (!Thread.use_pthreads) {
- if (startup.linux_tls_phdr) |tls_phdr| {
+ if (linux_tls_phdr) |tls_phdr| {
l = mem.alignForward(l, tls_phdr.p_align);
tls_start_offset = l;
l += tls_phdr.p_memsz;
@@ -3153,8 +3164,8 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
posix.CLONE_DETACHED;
var newtls: usize = undefined;
- if (startup.linux_tls_phdr) |tls_phdr| {
- @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), startup.linux_tls_img_src, tls_phdr.p_filesz);
+ if (linux_tls_phdr) |tls_phdr| {
+ @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), linux_tls_img_src, tls_phdr.p_filesz);
thread_ptr.data.tls_end_addr = mmap_addr + mmap_len;
newtls = @ptrToInt(&thread_ptr.data.tls_end_addr);
flags |= posix.CLONE_SETTLS;
diff --git a/std/os/startup.zig b/std/os/startup.zig
deleted file mode 100644
index c54d274c5d..0000000000
--- a/std/os/startup.zig
+++ /dev/null
@@ -1,26 +0,0 @@
-// This file contains global variables that are initialized on startup from
-// std/special/bootstrap.zig. There are a few things to be aware of here.
-//
-// First, when building an object or library, and no entry point is defined
-// (such as pub fn main), std/special/bootstrap.zig is not included in the
-// compilation. And so these global variables will remain set to the values
-// you see here.
-//
-// Second, when using `zig test` to test the standard library, note that
-// `zig test` is self-hosted. This means that it uses std/special/bootstrap.zig
-// and an @import("std") from the install directory, which is distinct from
-// the standard library files that we are directly testing with `zig test`.
-// This means that these global variables would not get set. So the workaround
-// here is that references to these globals from the standard library must
-// use `@import("std").startup` rather than
-// `@import("path/to/std/index.zig").startup` (and rather than the file path of
-// this file directly). We also put "std" as a reference to itself in the
-// standard library package so that this can work.
-
-const std = @import("../index.zig");
-
-pub var linux_tls_phdr: ?*std.elf.Phdr = null;
-pub var linux_tls_img_src: [*]const u8 = undefined; // defined when linux_tls_phdr is non-null
-pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null;
-pub var posix_environ_raw: [][*]u8 = undefined;
-pub var posix_argv_raw: [][*]u8 = undefined;
diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig
index 0e84f67481..97699e0cc5 100644
--- a/std/special/bootstrap.zig
+++ b/std/special/bootstrap.zig
@@ -64,7 +64,7 @@ fn posixCallMainAndExit() noreturn {
if (builtin.os == builtin.Os.linux) {
// Scan auxiliary vector.
const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
- std.startup.linux_elf_aux_maybe = auxv;
+ std.os.linux_elf_aux_maybe = auxv;
var i: usize = 0;
var at_phdr: usize = 0;
var at_phnum: usize = 0;
@@ -87,8 +87,8 @@ fn posixCallMainAndExit() noreturn {
// This is marked inline because for some reason LLVM in release mode fails to inline it,
// and we want fewer call frames in stack traces.
inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {
- std.startup.posix_argv_raw = argv[0..argc];
- std.startup.posix_environ_raw = envp;
+ std.os.ArgIteratorPosix.raw = argv[0..argc];
+ std.os.posix_environ_raw = envp;
return callMain();
}
@@ -145,15 +145,15 @@ fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent:
// TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917
switch (phdr.p_type) {
std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr,
- std.elf.PT_TLS => std.startup.linux_tls_phdr = phdr,
+ std.elf.PT_TLS => std.os.linux_tls_phdr = phdr,
else => continue,
}
}
- const tls_phdr = std.startup.linux_tls_phdr orelse return;
- std.startup.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr);
+ const tls_phdr = std.os.linux_tls_phdr orelse return;
+ std.os.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr);
assert(main_thread_tls_bytes.len >= tls_phdr.p_memsz); // not enough preallocated Thread Local Storage
assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough
- @memcpy(&main_thread_tls_bytes, std.startup.linux_tls_img_src, tls_phdr.p_filesz);
+ @memcpy(&main_thread_tls_bytes, std.os.linux_tls_img_src, tls_phdr.p_filesz);
tls_end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz;
linuxSetThreadArea(@ptrToInt(&tls_end_addr));
}
diff --git a/test/tests.zig b/test/tests.zig
index 548496fa2f..fc188f5550 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -194,6 +194,9 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons
if (link_libc) {
these_tests.linkSystemLibrary("c");
}
+ if (mem.eql(u8, name, "std")) {
+ these_tests.overrideStdDir("std");
+ }
step.dependOn(&these_tests.step);
}
}
--
cgit v1.2.3
From 89ffb5819703c81514e4f29d2cadb5b74ea1f840 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 6 Feb 2019 18:32:41 -0500
Subject: implement Thread Local Storage on Windows
---
CMakeLists.txt | 1 +
src/codegen.cpp | 14 ++++++++------
std/os/windows/index.zig | 19 ++++++++++++++++++-
std/os/windows/kernel32.zig | 4 ++++
std/os/windows/tls.zig | 36 ++++++++++++++++++++++++++++++++++++
std/special/bootstrap.zig | 4 +++-
6 files changed, 70 insertions(+), 8 deletions(-)
create mode 100644 std/os/windows/tls.zig
(limited to 'src/codegen.cpp')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4dd6a1dcfa..d6bd8a6c2e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -596,6 +596,7 @@ set(ZIG_STD_FILES
"os/windows/ntdll.zig"
"os/windows/ole32.zig"
"os/windows/shell32.zig"
+ "os/windows/tls.zig"
"os/windows/util.zig"
"os/zen.zig"
"pdb.zig"
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 5e282160d6..7169669426 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -6365,6 +6365,12 @@ static void validate_inline_fns(CodeGen *g) {
report_errors_and_maybe_exit(g);
}
+static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) {
+ if (var->is_thread_local && !g->is_single_threaded) {
+ LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
+ }
+}
+
static void do_code_gen(CodeGen *g) {
assert(!g->errors.length);
@@ -6449,9 +6455,7 @@ static void do_code_gen(CodeGen *g) {
maybe_import_dll(g, global_value, GlobalLinkageIdStrong);
LLVMSetAlignment(global_value, var->align_bytes);
LLVMSetGlobalConstant(global_value, var->gen_is_const);
- if (var->is_thread_local && !g->is_single_threaded) {
- LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
- }
+ set_global_tls(g, var, global_value);
}
} else {
bool exported = (var->linkage == VarLinkageExport);
@@ -6477,9 +6481,7 @@ static void do_code_gen(CodeGen *g) {
}
LLVMSetGlobalConstant(global_value, var->gen_is_const);
- if (var->is_thread_local && !g->is_single_threaded) {
- LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
- }
+ set_global_tls(g, var, global_value);
}
var->value_ref = global_value;
diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig
index 3f19905835..8e9ed8b8db 100644
--- a/std/os/windows/index.zig
+++ b/std/os/windows/index.zig
@@ -49,7 +49,10 @@ pub const UNICODE = false;
pub const WCHAR = u16;
pub const WORD = u16;
pub const LARGE_INTEGER = i64;
-pub const LONG = c_long;
+pub const ULONG = u32;
+pub const LONG = i32;
+pub const ULONGLONG = u64;
+pub const LONGLONG = i64;
pub const TRUE = 1;
pub const FALSE = 0;
@@ -380,3 +383,17 @@ pub const COORD = extern struct {
};
pub const CREATE_UNICODE_ENVIRONMENT = 1024;
+
+pub const TLS_OUT_OF_INDEXES = 4294967295;
+pub const IMAGE_TLS_DIRECTORY = extern struct {
+ StartAddressOfRawData: usize,
+ EndAddressOfRawData: usize,
+ AddressOfIndex: usize,
+ AddressOfCallBacks: usize,
+ SizeOfZeroFill: u32,
+ Characteristics: u32,
+};
+pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY;
+pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;
+
+pub const PIMAGE_TLS_CALLBACK = ?extern fn(PVOID, DWORD, PVOID) void;
diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig
index 66b9552c5f..ce8443de02 100644
--- a/std/os/windows/kernel32.zig
+++ b/std/os/windows/kernel32.zig
@@ -164,6 +164,10 @@ pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
+pub extern "kernel32" stdcallcc fn TlsAlloc() DWORD;
+
+pub extern "kernel32" stdcallcc fn TlsFree(dwTlsIndex: DWORD) BOOL;
+
pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn WriteFile(
diff --git a/std/os/windows/tls.zig b/std/os/windows/tls.zig
new file mode 100644
index 0000000000..9e62a7c5c6
--- /dev/null
+++ b/std/os/windows/tls.zig
@@ -0,0 +1,36 @@
+const std = @import("../../index.zig");
+
+export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES;
+export var _tls_start: u8 linksection(".tls") = 0;
+export var _tls_end: u8 linksection(".tls$ZZZ") = 0;
+export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null;
+export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null;
+
+// TODO this is how I would like it to be expressed
+// TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate
+// why they do that.
+//export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY {
+// .StartAddressOfRawData = @ptrToInt(&_tls_start),
+// .EndAddressOfRawData = @ptrToInt(&_tls_end),
+// .AddressOfIndex = @ptrToInt(&_tls_index),
+// .AddressOfCallBacks = @ptrToInt(__xl_a),
+// .SizeOfZeroFill = 0,
+// .Characteristics = 0,
+//};
+// This is the workaround because we can't do @ptrToInt at comptime like that.
+pub const IMAGE_TLS_DIRECTORY = extern struct {
+ StartAddressOfRawData: *c_void,
+ EndAddressOfRawData: *c_void,
+ AddressOfIndex: *c_void,
+ AddressOfCallBacks: *c_void,
+ SizeOfZeroFill: u32,
+ Characteristics: u32,
+};
+export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY {
+ .StartAddressOfRawData = &_tls_start,
+ .EndAddressOfRawData = &_tls_end,
+ .AddressOfIndex = &_tls_index,
+ .AddressOfCallBacks = &__xl_a,
+ .SizeOfZeroFill = 0,
+ .Characteristics = 0,
+};
diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig
index 97699e0cc5..6dcc90b372 100644
--- a/std/special/bootstrap.zig
+++ b/std/special/bootstrap.zig
@@ -45,7 +45,9 @@ nakedcc fn _start() noreturn {
extern fn WinMainCRTStartup() noreturn {
@setAlignStack(16);
-
+ if (!builtin.single_threaded) {
+ _ = @import("../os/windows/tls.zig");
+ }
std.os.windows.ExitProcess(callMain());
}
--
cgit v1.2.3
From 2b2bf53a49616192e2b2bdf40b88400964ff1500 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 7 Feb 2019 11:40:56 -0500
Subject: better error message when LLVM does not understand a triple
---
src/codegen.cpp | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
(limited to 'src/codegen.cpp')
diff --git a/src/codegen.cpp b/src/codegen.cpp
index c5de05e372..f010430e14 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -7553,7 +7553,13 @@ static void init(CodeGen *g) {
LLVMTargetRef target_ref;
char *err_msg = nullptr;
if (LLVMGetTargetFromTriple(buf_ptr(&g->triple_str), &target_ref, &err_msg)) {
- zig_panic("unable to create target based on: %s", buf_ptr(&g->triple_str));
+ fprintf(stderr,
+ "Zig is expecting LLVM to understand this target: '%s'\n"
+ "However LLVM responded with: \"%s\"\n"
+ "Zig is unable to continue. This is a bug in Zig:\n"
+ "https://github.com/ziglang/zig/issues/438\n"
+ , buf_ptr(&g->triple_str), err_msg);
+ exit(1);
}
bool is_optimized = g->build_mode != BuildModeDebug;
--
cgit v1.2.3
From f330eebe4bc6a036846cf05706f72855627c705a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 7 Feb 2019 16:02:45 -0500
Subject: fix using the result of @intCast to u0
closes #1817
---
src/all_types.hpp | 7 +++++++
src/codegen.cpp | 40 +++++++++++++++++++++++++++++-----------
src/ir.cpp | 31 +++++++++++++++++++++++++++++++
src/ir_print.cpp | 9 +++++++++
test/runtime_safety.zig | 17 +++++++++++++++++
test/stage1/behavior/cast.zig | 11 +++++++++++
test/stage1/behavior/eval.zig | 6 ------
7 files changed, 104 insertions(+), 17 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 5af4e71157..842c9ae904 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -2239,6 +2239,7 @@ enum IrInstructionId {
IrInstructionIdCheckRuntimeScope,
IrInstructionIdVectorToArray,
IrInstructionIdArrayToVector,
+ IrInstructionIdAssertZero,
};
struct IrInstruction {
@@ -3381,6 +3382,12 @@ struct IrInstructionVectorToArray {
LLVMValueRef tmp_ptr;
};
+struct IrInstructionAssertZero {
+ IrInstruction base;
+
+ IrInstruction *target;
+};
+
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;
diff --git a/src/codegen.cpp b/src/codegen.cpp
index f010430e14..3bfd7cdfc5 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -1651,10 +1651,25 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val,
LLVMPositionBuilderAtEnd(g->builder, ok_block);
}
+static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) {
+ LLVMValueRef zero = LLVMConstNull(int_type->type_ref);
+ LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, "");
+ LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk");
+ LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail");
+ LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
+
+ LLVMPositionBuilderAtEnd(g->builder, fail_block);
+ gen_safety_crash(g, PanicMsgIdCastTruncatedData);
+
+ LLVMPositionBuilderAtEnd(g->builder, ok_block);
+ return nullptr;
+}
+
static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, ZigType *actual_type,
ZigType *wanted_type, LLVMValueRef expr_val)
{
assert(actual_type->id == wanted_type->id);
+ assert(expr_val != nullptr);
uint64_t actual_bits;
uint64_t wanted_bits;
@@ -1707,17 +1722,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
if (!want_runtime_safety)
return nullptr;
- LLVMValueRef zero = LLVMConstNull(actual_type->type_ref);
- LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, "");
- LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk");
- LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail");
- LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
-
- LLVMPositionBuilderAtEnd(g->builder, fail_block);
- gen_safety_crash(g, PanicMsgIdCastTruncatedData);
-
- LLVMPositionBuilderAtEnd(g->builder, ok_block);
- return nullptr;
+ return gen_assert_zero(g, expr_val, actual_type);
}
LLVMValueRef trunc_val = LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, "");
if (!want_runtime_safety) {
@@ -5209,6 +5214,17 @@ static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executab
return gen_load_untyped(g, casted_ptr, 0, false, "");
}
+static LLVMValueRef ir_render_assert_zero(CodeGen *g, IrExecutable *executable,
+ IrInstructionAssertZero *instruction)
+{
+ LLVMValueRef target = ir_llvm_value(g, instruction->target);
+ ZigType *int_type = instruction->target->value.type;
+ if (ir_want_runtime_safety(g, &instruction->base)) {
+ return gen_assert_zero(g, target, int_type);
+ }
+ return nullptr;
+}
+
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
AstNode *source_node = instruction->source_node;
Scope *scope = instruction->scope;
@@ -5458,6 +5474,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction);
case IrInstructionIdVectorToArray:
return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction);
+ case IrInstructionIdAssertZero:
+ return ir_render_assert_zero(g, executable, (IrInstructionAssertZero *)instruction);
}
zig_unreachable();
}
diff --git a/src/ir.cpp b/src/ir.cpp
index 02b2b12230..00d358552a 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -908,6 +908,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToVector *)
return IrInstructionIdArrayToVector;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertZero *) {
+ return IrInstructionIdAssertZero;
+}
+
template
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate(1);
@@ -2858,6 +2862,19 @@ static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *so
return &instruction->base;
}
+static IrInstruction *ir_build_assert_zero(IrAnalyze *ira, IrInstruction *source_instruction,
+ IrInstruction *target)
+{
+ IrInstructionAssertZero *instruction = ir_build_instruction(&ira->new_irb,
+ source_instruction->scope, source_instruction->source_node);
+ instruction->base.value.type = ira->codegen->builtin_types.entry_void;
+ instruction->target = target;
+
+ ir_ref_instruction(target, ira->new_irb.current_basic_block);
+
+ return &instruction->base;
+}
+
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@@ -10395,6 +10412,18 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction
return result;
}
+ // If the destination integer type has no bits, then we can emit a comptime
+ // zero. However, we still want to emit a runtime safety check to make sure
+ // the target is zero.
+ if (!type_has_bits(wanted_type)) {
+ assert(wanted_type->id == ZigTypeIdInt);
+ assert(type_has_bits(target->value.type));
+ ir_build_assert_zero(ira, source_instr, target);
+ IrInstruction *result = ir_const_unsigned(ira, source_instr, 0);
+ result->value.type = wanted_type;
+ return result;
+ }
+
IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
source_instr->source_node, target);
result->value.type = wanted_type;
@@ -21705,6 +21734,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
case IrInstructionIdCmpxchgGen:
case IrInstructionIdArrayToVector:
case IrInstructionIdVectorToArray:
+ case IrInstructionIdAssertZero:
zig_unreachable();
case IrInstructionIdReturn:
@@ -22103,6 +22133,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdAtomicRmw:
case IrInstructionIdCmpxchgGen:
case IrInstructionIdCmpxchgSrc:
+ case IrInstructionIdAssertZero:
return true;
case IrInstructionIdPhi:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index e19aa6dda8..75da24d1a9 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -984,6 +984,12 @@ static void ir_print_vector_to_array(IrPrint *irp, IrInstructionVectorToArray *i
fprintf(irp->f, ")");
}
+static void ir_print_assert_zero(IrPrint *irp, IrInstructionAssertZero *instruction) {
+ fprintf(irp->f, "AssertZero(");
+ ir_print_other_instruction(irp, instruction->target);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) {
fprintf(irp->f, "inttoerr ");
ir_print_other_instruction(irp, instruction->target);
@@ -1843,6 +1849,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdVectorToArray:
ir_print_vector_to_array(irp, (IrInstructionVectorToArray *)instruction);
break;
+ case IrInstructionIdAssertZero:
+ ir_print_assert_zero(irp, (IrInstructionAssertZero *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig
index 7c13f5b6fa..7de43b45f4 100644
--- a/test/runtime_safety.zig
+++ b/test/runtime_safety.zig
@@ -362,6 +362,23 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\}
);
+ // @intCast a runtime integer to u0 actually results in a comptime-known value,
+ // but we still emit a safety check to ensure the integer was 0 and thus
+ // did not truncate information.
+ cases.addRuntimeSafety("@intCast to u0",
+ \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
+ \\ @import("std").os.exit(126);
+ \\}
+ \\
+ \\pub fn main() void {
+ \\ bar(1, 1);
+ \\}
+ \\
+ \\fn bar(one: u1, not_zero: i32) void {
+ \\ var x = one << @intCast(u0, not_zero);
+ \\}
+ );
+
// This case makes sure that the code compiles and runs. There is not actually a special
// runtime safety check having to do specifically with error return traces across suspend points.
cases.addRuntimeSafety("error return trace across suspend points",
diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig
index 61ddcd8135..27f685a96e 100644
--- a/test/stage1/behavior/cast.zig
+++ b/test/stage1/behavior/cast.zig
@@ -471,3 +471,14 @@ test "@intToEnum passed a comptime_int to an enum with one item" {
const x = @intToEnum(E, 0);
assertOrPanic(x == E.A);
}
+
+test "@intCast to u0 and use the result" {
+ const S = struct {
+ fn doTheTest(zero: u1, one: u1, bigzero: i32) void {
+ assertOrPanic((one << @intCast(u0, bigzero)) == 1);
+ assertOrPanic((zero << @intCast(u0, bigzero)) == 0);
+ }
+ };
+ S.doTheTest(0, 1, 0);
+ comptime S.doTheTest(0, 1, 0);
+}
diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig
index 3e8af0524f..0d1ecfab5b 100644
--- a/test/stage1/behavior/eval.zig
+++ b/test/stage1/behavior/eval.zig
@@ -697,12 +697,6 @@ test "bit shift a u1" {
assertOrPanic(y == 1);
}
-test "@intCast to a u0" {
- var x: u8 = 0;
- var y: u0 = @intCast(u0, x);
- assertOrPanic(y == 0);
-}
-
test "@bytesToslice on a packed struct" {
const F = packed struct {
a: u8,
--
cgit v1.2.3
From 0a7bdc00771dbad1dfe5eb93a7cade89059d227a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 9 Feb 2019 14:44:33 -0500
Subject: implement vector addition with safety checking
this would work if @llvm.sadd.with.overflow supported
vectors, which it does in trunk. but it does not support
them in llvm 7 or even in llvm 8 release branch.
so the next commit after this will have to do a different
strategy, but when llvm 9 comes out it may be worth coming
back to this one.
---
src/all_types.hpp | 3 ++
src/analyze.cpp | 6 ++-
src/codegen.cpp | 154 +++++++++++++++++++++++++++++++-----------------------
3 files changed, 95 insertions(+), 68 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 842c9ae904..908c0e327c 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1538,6 +1538,8 @@ enum ZigLLVMFnId {
ZigLLVMFnIdBitReverse,
};
+// There are a bunch of places in code that rely on these values being in
+// exactly this order.
enum AddSubMul {
AddSubMulAdd = 0,
AddSubMulSub = 1,
@@ -1563,6 +1565,7 @@ struct ZigLLVMFnKey {
struct {
AddSubMul add_sub_mul;
uint32_t bit_count;
+ uint32_t vector_len; // 0 means not a vector
bool is_signed;
} overflow_arithmetic;
struct {
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 83a576554a..0c493ebda1 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -6361,7 +6361,8 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
case ZigLLVMFnIdOverflowArithmetic:
return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
- ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820);
+ ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820) +
+ x.data.overflow_arithmetic.vector_len * 1435156945;
}
zig_unreachable();
}
@@ -6387,7 +6388,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
case ZigLLVMFnIdOverflowArithmetic:
return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
(a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
- (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed);
+ (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed) &&
+ (a.data.overflow_arithmetic.vector_len == b.data.overflow_arithmetic.vector_len);
}
zig_unreachable();
}
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 3bfd7cdfc5..e45280b0d1 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -715,38 +715,59 @@ static void clear_debug_source_node(CodeGen *g) {
ZigLLVMClearCurrentDebugLocation(g->builder);
}
-static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *type_entry,
+static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *operand_type,
const char *signed_name, const char *unsigned_name)
{
+ ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
char fn_name[64];
- assert(type_entry->id == ZigTypeIdInt);
- const char *signed_str = type_entry->data.integral.is_signed ? signed_name : unsigned_name;
- sprintf(fn_name, "llvm.%s.with.overflow.i%" PRIu32, signed_str, type_entry->data.integral.bit_count);
+ assert(int_type->id == ZigTypeIdInt);
+ const char *signed_str = int_type->data.integral.is_signed ? signed_name : unsigned_name;
- LLVMTypeRef return_elem_types[] = {
- type_entry->type_ref,
- LLVMInt1Type(),
- };
LLVMTypeRef param_types[] = {
- type_entry->type_ref,
- type_entry->type_ref,
+ operand_type->type_ref,
+ operand_type->type_ref,
};
- LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
- LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
- LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type);
- assert(LLVMGetIntrinsicID(fn_val));
- return fn_val;
+
+ if (operand_type->id == ZigTypeIdVector) {
+ sprintf(fn_name, "llvm.%s.with.overflow.v%" PRIu32 "i%" PRIu32, signed_str,
+ operand_type->data.vector.len, int_type->data.integral.bit_count);
+
+ LLVMTypeRef return_elem_types[] = {
+ operand_type->type_ref,
+ LLVMVectorType(LLVMInt1Type(), operand_type->data.vector.len),
+ };
+ LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
+ LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
+ LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type);
+ assert(LLVMGetIntrinsicID(fn_val));
+ return fn_val;
+ } else {
+ sprintf(fn_name, "llvm.%s.with.overflow.i%" PRIu32, signed_str, int_type->data.integral.bit_count);
+
+ LLVMTypeRef return_elem_types[] = {
+ operand_type->type_ref,
+ LLVMInt1Type(),
+ };
+ LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
+ LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
+ LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type);
+ assert(LLVMGetIntrinsicID(fn_val));
+ return fn_val;
+ }
}
-static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubMul add_sub_mul) {
- assert(type_entry->id == ZigTypeIdInt);
+static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSubMul add_sub_mul) {
+ ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
+ assert(int_type->id == ZigTypeIdInt);
ZigLLVMFnKey key = {};
key.id = ZigLLVMFnIdOverflowArithmetic;
- key.data.overflow_arithmetic.is_signed = type_entry->data.integral.is_signed;
+ key.data.overflow_arithmetic.is_signed = int_type->data.integral.is_signed;
key.data.overflow_arithmetic.add_sub_mul = add_sub_mul;
- key.data.overflow_arithmetic.bit_count = (uint32_t)type_entry->data.integral.bit_count;
+ key.data.overflow_arithmetic.bit_count = (uint32_t)int_type->data.integral.bit_count;
+ key.data.overflow_arithmetic.vector_len = (operand_type->id == ZigTypeIdVector) ?
+ operand_type->data.vector.len : 0;
auto existing_entry = g->llvm_fn_table.maybe_get(key);
if (existing_entry)
@@ -755,13 +776,13 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubM
LLVMValueRef fn_val;
switch (add_sub_mul) {
case AddSubMulAdd:
- fn_val = get_arithmetic_overflow_fn(g, type_entry, "sadd", "uadd");
+ fn_val = get_arithmetic_overflow_fn(g, operand_type, "sadd", "uadd");
break;
case AddSubMulSub:
- fn_val = get_arithmetic_overflow_fn(g, type_entry, "ssub", "usub");
+ fn_val = get_arithmetic_overflow_fn(g, operand_type, "ssub", "usub");
break;
case AddSubMulMul:
- fn_val = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul");
+ fn_val = get_arithmetic_overflow_fn(g, operand_type, "smul", "umul");
break;
}
@@ -1752,17 +1773,28 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
}
}
-static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *type_entry, AddSubMul op,
+static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op,
LLVMValueRef val1, LLVMValueRef val2)
{
- LLVMValueRef fn_val = get_int_overflow_fn(g, type_entry, op);
+ LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op);
LLVMValueRef params[] = {
val1,
val2,
};
LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
- LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
+
+ LLVMValueRef overflow_bit;
+ if (operand_type->id == ZigTypeIdVector) {
+ LLVMValueRef overflow_vector = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
+ LLVMTypeRef bigger_int_type_ref = LLVMIntType(operand_type->data.vector.len);
+ LLVMValueRef bitcasted_overflow = LLVMBuildBitCast(g->builder, overflow_vector, bigger_int_type_ref, "");
+ LLVMValueRef zero = LLVMConstNull(bigger_int_type_ref);
+ overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, bitcasted_overflow, zero, "");
+ } else {
+ overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
+ }
+
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail");
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk");
LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block);
@@ -2608,7 +2640,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
(op_id == IrBinOpAdd || op_id == IrBinOpSub) &&
op1->value.type->data.pointer.ptr_len == PtrLenUnknown)
);
- ZigType *type_entry = op1->value.type;
+ ZigType *operand_type = op1->value.type;
+ ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
bool want_runtime_safety = bin_op_instruction->safety_check_on &&
ir_want_runtime_safety(g, &bin_op_instruction->base);
@@ -2634,17 +2667,17 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
case IrBinOpCmpGreaterThan:
case IrBinOpCmpLessOrEq:
case IrBinOpCmpGreaterOrEq:
- if (type_entry->id == ZigTypeIdFloat) {
+ if (scalar_type->id == ZigTypeIdFloat) {
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id);
return LLVMBuildFCmp(g->builder, pred, op1_value, op2_value, "");
- } else if (type_entry->id == ZigTypeIdInt) {
- LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, type_entry->data.integral.is_signed);
+ } else if (scalar_type->id == ZigTypeIdInt) {
+ LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, scalar_type->data.integral.is_signed);
return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
- } else if (type_entry->id == ZigTypeIdEnum ||
- type_entry->id == ZigTypeIdErrorSet ||
- type_entry->id == ZigTypeIdBool ||
- get_codegen_ptr_type(type_entry) != nullptr)
+ } else if (scalar_type->id == ZigTypeIdEnum ||
+ scalar_type->id == ZigTypeIdErrorSet ||
+ scalar_type->id == ZigTypeIdBool ||
+ get_codegen_ptr_type(scalar_type) != nullptr)
{
LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
@@ -2665,23 +2698,16 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
- bool is_vector = type_entry->id == ZigTypeIdVector;
bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap);
AddSubMul add_sub_mul =
op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :
op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub :
AddSubMulMul;
- // The code that is generated for vectors and scalars are the same,
- // so we can just set type_entry to the vectors elem_type an avoid
- // a lot of repeated code.
- if (is_vector)
- type_entry = type_entry->data.vector.elem_type;
-
- if (type_entry->id == ZigTypeIdPointer) {
- assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
+ if (scalar_type->id == ZigTypeIdPointer) {
+ assert(scalar_type->data.pointer.ptr_len == PtrLenUnknown);
LLVMValueRef subscript_value;
- if (is_vector)
+ if (operand_type->id == ZigTypeIdVector)
zig_panic("TODO: Implement vector operations on pointers.");
switch (add_sub_mul) {
@@ -2697,17 +2723,15 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
// TODO runtime safety
return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
- } else if (type_entry->id == ZigTypeIdFloat) {
+ } else if (scalar_type->id == ZigTypeIdFloat) {
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
return float_op[add_sub_mul](g->builder, op1_value, op2_value, "");
- } else if (type_entry->id == ZigTypeIdInt) {
+ } else if (scalar_type->id == ZigTypeIdInt) {
if (is_wrapping) {
return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, "");
} else if (want_runtime_safety) {
- if (is_vector)
- zig_panic("TODO: Implement runtime safety vector operations.");
- return gen_overflow_op(g, type_entry, add_sub_mul, op1_value, op2_value);
- } else if (type_entry->data.integral.is_signed) {
+ return gen_overflow_op(g, operand_type, add_sub_mul, op1_value, op2_value);
+ } else if (scalar_type->data.integral.is_signed) {
return signed_op[add_sub_mul](g->builder, op1_value, op2_value, "");
} else {
return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, "");
@@ -2725,15 +2749,14 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
case IrBinOpBitShiftLeftLossy:
case IrBinOpBitShiftLeftExact:
{
- assert(type_entry->id == ZigTypeIdInt);
- LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type,
- type_entry, op2_value);
+ assert(scalar_type->id == ZigTypeIdInt);
+ LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, scalar_type, op2_value);
bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy);
if (is_sloppy) {
return LLVMBuildShl(g->builder, op1_value, op2_casted, "");
} else if (want_runtime_safety) {
- return gen_overflow_shl_op(g, type_entry, op1_value, op2_casted);
- } else if (type_entry->data.integral.is_signed) {
+ return gen_overflow_shl_op(g, scalar_type, op1_value, op2_casted);
+ } else if (scalar_type->data.integral.is_signed) {
return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, "");
} else {
return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_casted, "");
@@ -2742,19 +2765,18 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
case IrBinOpBitShiftRightLossy:
case IrBinOpBitShiftRightExact:
{
- assert(type_entry->id == ZigTypeIdInt);
- LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type,
- type_entry, op2_value);
+ assert(scalar_type->id == ZigTypeIdInt);
+ LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, scalar_type, op2_value);
bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy);
if (is_sloppy) {
- if (type_entry->data.integral.is_signed) {
+ if (scalar_type->data.integral.is_signed) {
return LLVMBuildAShr(g->builder, op1_value, op2_casted, "");
} else {
return LLVMBuildLShr(g->builder, op1_value, op2_casted, "");
}
} else if (want_runtime_safety) {
- return gen_overflow_shr_op(g, type_entry, op1_value, op2_casted);
- } else if (type_entry->data.integral.is_signed) {
+ return gen_overflow_shr_op(g, scalar_type, op1_value, op2_casted);
+ } else if (scalar_type->data.integral.is_signed) {
return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, "");
} else {
return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, "");
@@ -2762,22 +2784,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
}
case IrBinOpDivUnspecified:
return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
- op1_value, op2_value, type_entry, DivKindFloat);
+ op1_value, op2_value, scalar_type, DivKindFloat);
case IrBinOpDivExact:
return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
- op1_value, op2_value, type_entry, DivKindExact);
+ op1_value, op2_value, scalar_type, DivKindExact);
case IrBinOpDivTrunc:
return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
- op1_value, op2_value, type_entry, DivKindTrunc);
+ op1_value, op2_value, scalar_type, DivKindTrunc);
case IrBinOpDivFloor:
return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
- op1_value, op2_value, type_entry, DivKindFloor);
+ op1_value, op2_value, scalar_type, DivKindFloor);
case IrBinOpRemRem:
return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
- op1_value, op2_value, type_entry, RemKindRem);
+ op1_value, op2_value, scalar_type, RemKindRem);
case IrBinOpRemMod:
return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
- op1_value, op2_value, type_entry, RemKindMod);
+ op1_value, op2_value, scalar_type, RemKindMod);
}
zig_unreachable();
}
--
cgit v1.2.3
From 373e21bb564a27c4292812bdfd1673711c2e0fe4 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 9 Feb 2019 15:23:29 -0500
Subject: implement vector math safety with ext and trunc
---
src/codegen.cpp | 55 ++++++++++++++++++++++++++-----------------
test/runtime_safety.zig | 14 +++++++++++
test/stage1/behavior/math.zig | 17 +++++++++++++
3 files changed, 64 insertions(+), 22 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/codegen.cpp b/src/codegen.cpp
index e45280b0d1..4868576b49 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -1773,25 +1773,46 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
}
}
+typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
+// These are lookup table using the AddSubMul enum as the lookup.
+// If AddSubMul ever changes, then these tables will be out of
+// date.
+static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul };
+static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul };
+static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
+static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
+
static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op,
LLVMValueRef val1, LLVMValueRef val2)
{
- LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op);
- LLVMValueRef params[] = {
- val1,
- val2,
- };
- LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
- LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
-
LLVMValueRef overflow_bit;
+ LLVMValueRef result;
+
if (operand_type->id == ZigTypeIdVector) {
- LLVMValueRef overflow_vector = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
- LLVMTypeRef bigger_int_type_ref = LLVMIntType(operand_type->data.vector.len);
- LLVMValueRef bitcasted_overflow = LLVMBuildBitCast(g->builder, overflow_vector, bigger_int_type_ref, "");
- LLVMValueRef zero = LLVMConstNull(bigger_int_type_ref);
+ ZigType *int_type = operand_type->data.vector.elem_type;
+ assert(int_type->id == ZigTypeIdInt);
+ LLVMTypeRef one_more_bit_int = LLVMIntType(int_type->data.integral.bit_count + 1);
+ LLVMTypeRef one_more_bit_int_vector = LLVMVectorType(one_more_bit_int, operand_type->data.vector.len);
+ const auto buildExtFn = int_type->data.integral.is_signed ? LLVMBuildSExt : LLVMBuildZExt;
+ LLVMValueRef extended1 = buildExtFn(g->builder, val1, one_more_bit_int_vector, "");
+ LLVMValueRef extended2 = buildExtFn(g->builder, val2, one_more_bit_int_vector, "");
+ LLVMValueRef extended_result = wrap_op[op](g->builder, extended1, extended2, "");
+ result = LLVMBuildTrunc(g->builder, extended_result, operand_type->type_ref, "");
+
+ LLVMValueRef re_extended_result = buildExtFn(g->builder, result, one_more_bit_int_vector, "");
+ LLVMValueRef overflow_vector = LLVMBuildICmp(g->builder, LLVMIntNE, extended_result, re_extended_result, "");
+ LLVMTypeRef bitcast_int_type = LLVMIntType(operand_type->data.vector.len);
+ LLVMValueRef bitcasted_overflow = LLVMBuildBitCast(g->builder, overflow_vector, bitcast_int_type, "");
+ LLVMValueRef zero = LLVMConstNull(bitcast_int_type);
overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, bitcasted_overflow, zero, "");
} else {
+ LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op);
+ LLVMValueRef params[] = {
+ val1,
+ val2,
+ };
+ LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
+ result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
}
@@ -2623,8 +2644,6 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
}
-typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
-
static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
IrInstructionBinOp *bin_op_instruction)
{
@@ -2690,14 +2709,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
case IrBinOpAddWrap:
case IrBinOpSub:
case IrBinOpSubWrap: {
- // These are lookup table using the AddSubMul enum as the lookup.
- // If AddSubMul ever changes, then these tables will be out of
- // date.
- static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul };
- static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul };
- static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
- static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
-
bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap);
AddSubMul add_sub_mul =
op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :
diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig
index 7de43b45f4..821328b7a6 100644
--- a/test/runtime_safety.zig
+++ b/test/runtime_safety.zig
@@ -94,6 +94,20 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\}
);
+ cases.addRuntimeSafety("vector integer addition overflow",
+ \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
+ \\ @import("std").os.exit(126);
+ \\}
+ \\pub fn main() void {
+ \\ var a: @Vector(4, i32) = []i32{ 1, 2, 2147483643, 4 };
+ \\ var b: @Vector(4, i32) = []i32{ 5, 6, 7, 8 };
+ \\ const x = add(a, b);
+ \\}
+ \\fn add(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {
+ \\ return a + b;
+ \\}
+ );
+
cases.addRuntimeSafety("integer subtraction overflow",
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig
index 0b88cc4497..36e81e11ed 100644
--- a/test/stage1/behavior/math.zig
+++ b/test/stage1/behavior/math.zig
@@ -1,5 +1,7 @@
const std = @import("std");
const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const expectEqualSlices = std.testing.expectEqualSlices;
const maxInt = std.math.maxInt;
const minInt = std.math.minInt;
@@ -498,3 +500,18 @@ test "comptime_int param and return" {
fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
return a + b;
}
+
+test "vector integer addition" {
+ const S = struct {
+ fn doTheTest() void {
+ var a: @Vector(4, i32) = []i32{ 1, 2, 3, 4 };
+ var b: @Vector(4, i32) = []i32{ 5, 6, 7, 8 };
+ var result = a + b;
+ var result_array: [4]i32 = result;
+ const expected = []i32{ 6, 8, 10, 12 };
+ expectEqualSlices(i32, &expected, &result_array);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
--
cgit v1.2.3
From 8e68d43ad373e643797209d59e6f10aa12b4c038 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 10 Feb 2019 10:58:00 -0500
Subject: avoid needlessly creating global constants
This deletes some legacy cruft, and produces leaner object files.
Example:
```
var x: i32 = 1234;
export fn entry() i32 {
return x;
}
```
This produces:
```
@x = internal unnamed_addr global i32 1234, align 4
@0 = internal unnamed_addr constant i32* @x, align 8
```
and @0 is never even used. After this commit, @0 is not produced.
This fixes a bug: Zig was creating invalid LLVM IR when one of these
globals that shouldn't exist takes the address of a thread local
variable. In LLVM 8.0.0rc2, it would produce a linker error. But
probably after my bug report is solved it will be caught by the IR
verifier.
https://bugs.llvm.org/show_bug.cgi?id=40652
---
src/codegen.cpp | 47 ++++++++++++++++-------------------------------
1 file changed, 16 insertions(+), 31 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 4868576b49..192c0f0519 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -5762,81 +5762,71 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
zig_unreachable();
case ConstPtrSpecialRef:
{
- render_const_val_global(g, const_val, name);
+ assert(const_val->global_refs != nullptr);
ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
render_const_val(g, pointee, "");
render_const_val_global(g, pointee, "");
- ConstExprValue *other_val = pointee;
- const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref);
- render_const_val_global(g, const_val, "");
+ const_val->global_refs->llvm_value = LLVMConstBitCast(pointee->global_refs->llvm_global, const_val->type->type_ref);
return const_val->global_refs->llvm_value;
}
case ConstPtrSpecialBaseArray:
{
- render_const_val_global(g, const_val, name);
+ assert(const_val->global_refs != nullptr);
ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
- size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
assert(array_const_val->type->id == ZigTypeIdArray);
- if (array_const_val->type->zero_bits) {
+ if (!type_has_bits(array_const_val->type)) {
// make this a null pointer
ZigType *usize = g->builtin_types.entry_usize;
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
const_val->type->type_ref);
- render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
- LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val,
- elem_index);
+ size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
+ LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, elem_index);
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
const_val->global_refs->llvm_value = ptr_val;
- render_const_val_global(g, const_val, "");
return ptr_val;
}
case ConstPtrSpecialBaseStruct:
{
- render_const_val_global(g, const_val, name);
+ assert(const_val->global_refs != nullptr);
ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val;
assert(struct_const_val->type->id == ZigTypeIdStruct);
- if (struct_const_val->type->zero_bits) {
+ if (!type_has_bits(struct_const_val->type)) {
// make this a null pointer
ZigType *usize = g->builtin_types.entry_usize;
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
const_val->type->type_ref);
- render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index;
- size_t gen_field_index =
- struct_const_val->type->data.structure.fields[src_field_index].gen_index;
+ size_t gen_field_index = struct_const_val->type->data.structure.fields[src_field_index].gen_index;
LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val,
gen_field_index);
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
const_val->global_refs->llvm_value = ptr_val;
- render_const_val_global(g, const_val, "");
return ptr_val;
}
case ConstPtrSpecialBaseErrorUnionCode:
{
- render_const_val_global(g, const_val, name);
+ assert(const_val->global_refs != nullptr);
ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_code.err_union_val;
assert(err_union_const_val->type->id == ZigTypeIdErrorUnion);
- if (err_union_const_val->type->zero_bits) {
+ if (!type_has_bits(err_union_const_val->type)) {
// make this a null pointer
ZigType *usize = g->builtin_types.entry_usize;
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
const_val->type->type_ref);
- render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_code_recursive(g, err_union_const_val);
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
const_val->global_refs->llvm_value = ptr_val;
- render_const_val_global(g, const_val, "");
return ptr_val;
}
case ConstPtrSpecialBaseErrorUnionPayload:
{
- render_const_val_global(g, const_val, name);
+ assert(const_val->global_refs != nullptr);
ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_payload.err_union_val;
assert(err_union_const_val->type->id == ZigTypeIdErrorUnion);
if (err_union_const_val->type->zero_bits) {
@@ -5844,18 +5834,16 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
ZigType *usize = g->builtin_types.entry_usize;
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
const_val->type->type_ref);
- render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_payload_recursive(g, err_union_const_val);
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
const_val->global_refs->llvm_value = ptr_val;
- render_const_val_global(g, const_val, "");
return ptr_val;
}
case ConstPtrSpecialBaseOptionalPayload:
{
- render_const_val_global(g, const_val, name);
+ assert(const_val->global_refs != nullptr);
ConstExprValue *optional_const_val = const_val->data.x_ptr.data.base_optional_payload.optional_val;
assert(optional_const_val->type->id == ZigTypeIdOptional);
if (optional_const_val->type->zero_bits) {
@@ -5863,23 +5851,20 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
ZigType *usize = g->builtin_types.entry_usize;
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
const_val->type->type_ref);
- render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
LLVMValueRef uncasted_ptr_val = gen_const_ptr_optional_payload_recursive(g, optional_const_val);
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
const_val->global_refs->llvm_value = ptr_val;
- render_const_val_global(g, const_val, "");
return ptr_val;
}
case ConstPtrSpecialHardCodedAddr:
{
- render_const_val_global(g, const_val, name);
+ assert(const_val->global_refs != nullptr);
uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr;
ZigType *usize = g->builtin_types.entry_usize;
- const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false),
- const_val->type->type_ref);
- render_const_val_global(g, const_val, "");
+ const_val->global_refs->llvm_value = LLVMConstIntToPtr(
+ LLVMConstInt(usize->type_ref, addr_value, false), const_val->type->type_ref);
return const_val->global_refs->llvm_value;
}
case ConstPtrSpecialFunction:
--
cgit v1.2.3
From 342bca7f4627454435e9f6c2d12b099f95a2fd47 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 11 Feb 2019 15:31:09 -0500
Subject: C pointer comparison and arithmetic
See #1059
---
src/analyze.cpp | 2 +-
src/codegen.cpp | 4 ++--
src/ir.cpp | 46 ++++++++++++++++++++++++++++++++++-----
src/translate_c.cpp | 8 ++++---
test/stage1/behavior/pointers.zig | 20 +++++++++++++++++
test/translate_c.zig | 18 +++++++--------
6 files changed, 78 insertions(+), 20 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 691579200e..af6200cc82 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -434,7 +434,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
uint32_t bit_offset_in_host, uint32_t host_int_bytes)
{
assert(!type_is_invalid(child_type));
- assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque);
+ assert(ptr_len != PtrLenUnknown || child_type->id != ZigTypeIdOpaque);
if (byte_alignment != 0) {
uint32_t abi_alignment = get_abi_alignment(g, child_type);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 4868576b49..605ea59b06 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -2657,7 +2657,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
(op1->value.type->id == ZigTypeIdErrorSet && op2->value.type->id == ZigTypeIdErrorSet) ||
(op1->value.type->id == ZigTypeIdPointer &&
(op_id == IrBinOpAdd || op_id == IrBinOpSub) &&
- op1->value.type->data.pointer.ptr_len == PtrLenUnknown)
+ op1->value.type->data.pointer.ptr_len != PtrLenSingle)
);
ZigType *operand_type = op1->value.type;
ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
@@ -2716,7 +2716,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
AddSubMulMul;
if (scalar_type->id == ZigTypeIdPointer) {
- assert(scalar_type->data.pointer.ptr_len == PtrLenUnknown);
+ assert(scalar_type->data.pointer.ptr_len != PtrLenSingle);
LLVMValueRef subscript_value;
if (operand_type->id == ZigTypeIdVector)
zig_panic("TODO: Implement vector operations on pointers.");
diff --git a/src/ir.cpp b/src/ir.cpp
index 30350d75de..bc37ac9b54 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -8943,7 +8943,9 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *
*errors = reallocate(*errors, old_errors_count, *errors_count);
}
-static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type, IrInstruction **instructions, size_t instruction_count) {
+static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type,
+ IrInstruction **instructions, size_t instruction_count)
+{
Error err;
assert(instruction_count >= 1);
IrInstruction *prev_inst = instructions[0];
@@ -9260,6 +9262,19 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
continue;
}
+ if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenC &&
+ (cur_type->id == ZigTypeIdComptimeInt || cur_type->id == ZigTypeIdInt))
+ {
+ continue;
+ }
+
+ if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenC &&
+ (prev_type->id == ZigTypeIdComptimeInt || prev_type->id == ZigTypeIdInt))
+ {
+ prev_inst = cur_inst;
+ continue;
+ }
+
if (types_match_const_cast_only(ira, prev_type, cur_type, source_node, false).id == ConstCastResultIdOk) {
continue;
}
@@ -11852,7 +11867,6 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
case ZigTypeIdBool:
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
- case ZigTypeIdPointer:
case ZigTypeIdErrorSet:
case ZigTypeIdFn:
case ZigTypeIdOpaque:
@@ -11864,6 +11878,10 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
operator_allowed = is_equality_cmp;
break;
+ case ZigTypeIdPointer:
+ operator_allowed = is_equality_cmp || (resolved_type->data.pointer.ptr_len != PtrLenSingle);
+ break;
+
case ZigTypeIdUnreachable:
case ZigTypeIdArray:
case ZigTypeIdStruct:
@@ -12324,6 +12342,26 @@ static bool ok_float_op(IrBinOp op) {
zig_unreachable();
}
+static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
+ if (lhs_type->id != ZigTypeIdPointer)
+ return false;
+ switch (op) {
+ case IrBinOpAdd:
+ case IrBinOpSub:
+ break;
+ default:
+ return false;
+ }
+ switch (lhs_type->data.pointer.ptr_len) {
+ case PtrLenSingle:
+ return false;
+ case PtrLenUnknown:
+ case PtrLenC:
+ break;
+ }
+ return true;
+}
+
static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *instruction) {
IrInstruction *op1 = instruction->op1->child;
if (type_is_invalid(op1->value.type))
@@ -12336,9 +12374,7 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
IrBinOp op_id = instruction->op_id;
// look for pointer math
- if (op1->value.type->id == ZigTypeIdPointer && op1->value.type->data.pointer.ptr_len == PtrLenUnknown &&
- (op_id == IrBinOpAdd || op_id == IrBinOpSub))
- {
+ if (is_pointer_arithmetic_allowed(op1->value.type, op_id)) {
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, ira->codegen->builtin_types.entry_usize);
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
diff --git a/src/translate_c.cpp b/src/translate_c.cpp
index b06a28d12d..63f04dae6c 100644
--- a/src/translate_c.cpp
+++ b/src/translate_c.cpp
@@ -1677,7 +1677,7 @@ static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const Im
return node;
}
case CK_NullToPointer:
- return trans_create_node(c, NodeTypeNullLiteral);
+ return trans_create_node_unsigned(c, 0);
case CK_Dependent:
emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dependent");
return nullptr;
@@ -2409,7 +2409,8 @@ static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *
case BuiltinType::Float16:
return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false));
case BuiltinType::NullPtr:
- return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral));
+ return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq,
+ trans_create_node_unsigned(c, 0));
case BuiltinType::Void:
case BuiltinType::Half:
@@ -2494,7 +2495,8 @@ static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *
break;
}
case Type::Pointer:
- return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral));
+ return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq,
+ trans_create_node_unsigned(c, 0));
case Type::Typedef:
{
diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig
index 79832bc316..63e4c314b1 100644
--- a/test/stage1/behavior/pointers.zig
+++ b/test/stage1/behavior/pointers.zig
@@ -56,3 +56,23 @@ test "implicit cast single item pointer to C pointer and back" {
z.* += 1;
expect(y == 12);
}
+
+test "C pointer comparison and arithmetic" {
+ var one: usize = 1;
+ var ptr1: [*c]u8 = 0;
+ var ptr2 = ptr1 + 10;
+ expect(ptr1 == 0);
+ expect(ptr1 >= 0);
+ expect(ptr1 <= 0);
+ expect(ptr1 < 1);
+ expect(ptr1 < one);
+ expect(1 > ptr1);
+ expect(one > ptr1);
+ expect(ptr1 < ptr2);
+ expect(ptr2 > ptr1);
+ expect(ptr2 >= 10);
+ expect(ptr2 == 10);
+ expect(ptr2 <= 10);
+ ptr2 -= 10;
+ expect(ptr1 == ptr2);
+}
diff --git a/test/translate_c.zig b/test/translate_c.zig
index 746fa60b18..b87b962edc 100644
--- a/test/translate_c.zig
+++ b/test/translate_c.zig
@@ -610,11 +610,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\pub export fn and_or_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
\\ if ((a != 0) and (b != 0)) return 0;
- \\ if ((b != 0) and (c != null)) return 1;
- \\ if ((a != 0) and (c != null)) return 2;
+ \\ if ((b != 0) and (c != 0)) return 1;
+ \\ if ((a != 0) and (c != 0)) return 2;
\\ if ((a != 0) or (b != 0)) return 3;
- \\ if ((b != 0) or (c != null)) return 4;
- \\ if ((a != 0) or (c != null)) return 5;
+ \\ if ((b != 0) or (c != 0)) return 4;
+ \\ if ((a != 0) or (c != 0)) return 5;
\\ return 6;
\\}
);
@@ -778,7 +778,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
,
\\pub export fn foo() [*c]c_int {
- \\ return null;
+ \\ return 0;
\\}
);
@@ -1280,7 +1280,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return !(a == 0);
\\ return !(a != 0);
\\ return !(b != 0);
- \\ return !(c != null);
+ \\ return !(c != 0);
\\}
);
@@ -1337,7 +1337,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub fn if_none_bool(a: c_int, b: f32, c: [*c]c_void, d: enum_SomeEnum) c_int {
\\ if (a != 0) return 0;
\\ if (b != 0) return 1;
- \\ if (c != null) return 2;
+ \\ if (c != 0) return 2;
\\ if (d != @bitCast(enum_SomeEnum, @TagType(enum_SomeEnum)(0))) return 3;
\\ return 4;
\\}
@@ -1354,7 +1354,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub fn while_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
\\ while (a != 0) return 0;
\\ while (b != 0) return 1;
- \\ while (c != null) return 2;
+ \\ while (c != 0) return 2;
\\ return 3;
\\}
);
@@ -1370,7 +1370,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub fn for_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
\\ while (a != 0) return 0;
\\ while (b != 0) return 1;
- \\ while (c != null) return 2;
+ \\ while (c != 0) return 2;
\\ return 3;
\\}
);
--
cgit v1.2.3
From 90b8cd4a45bcb2ca131b6ed6466f799aaa162d13 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 11 Feb 2019 16:07:40 -0500
Subject: add C pointer type to @typeInfo
See #1059
---
src-self-hosted/type.zig | 2 ++
src/codegen.cpp | 1 +
src/ir.cpp | 14 +++++++++++++-
std/fmt/index.zig | 3 +++
std/meta/index.zig | 9 ++++++---
std/testing.zig | 3 +--
test/stage1/behavior/type_info.zig | 15 +++++++++++++++
7 files changed, 41 insertions(+), 6 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig
index 8a05594b30..790b51b7be 100644
--- a/src-self-hosted/type.zig
+++ b/src-self-hosted/type.zig
@@ -794,6 +794,7 @@ pub const Type = struct {
Size.One => "*",
Size.Many => "[*]",
Size.Slice => "[]",
+ Size.C => "[*c]",
};
const mut_str = switch (self.key.mut) {
Mut.Const => "const ",
@@ -1088,6 +1089,7 @@ fn hashAny(x: var, comptime seed: u64) u32 {
builtin.TypeInfo.Pointer.Size.One => return hashAny(@ptrToInt(x), seed),
builtin.TypeInfo.Pointer.Size.Many => @compileError("implement hash function"),
builtin.TypeInfo.Pointer.Size.Slice => @compileError("implement hash function"),
+ builtin.TypeInfo.Pointer.Size.C => unreachable,
}
},
builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed),
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 605ea59b06..142e8174f5 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -7309,6 +7309,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
" One,\n"
" Many,\n"
" Slice,\n"
+ " C,\n"
" };\n"
" };\n"
"\n"
diff --git a/src/ir.cpp b/src/ir.cpp
index bc37ac9b54..36ea50ed52 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -17584,6 +17584,18 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco
return ErrorNone;
}
+static uint32_t ptr_len_to_size_enum_index(PtrLen ptr_len) {
+ switch (ptr_len) {
+ case PtrLenSingle:
+ return 0;
+ case PtrLenUnknown:
+ return 1;
+ case PtrLenC:
+ return 3;
+ }
+ zig_unreachable();
+}
+
static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) {
Error err;
ZigType *attrs_type;
@@ -17593,7 +17605,7 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty
size_enum_index = 2;
} else if (ptr_type_entry->id == ZigTypeIdPointer) {
attrs_type = ptr_type_entry;
- size_enum_index = (ptr_type_entry->data.pointer.ptr_len == PtrLenSingle) ? 0 : 1;
+ size_enum_index = ptr_len_to_size_enum_index(ptr_type_entry->data.pointer.ptr_len);
} else {
zig_unreachable();
}
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index 05b028112f..b09fe21032 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -236,6 +236,9 @@ pub fn formatType(
const casted_value = ([]const u8)(value);
return output(context, casted_value);
},
+ builtin.TypeInfo.Pointer.Size.C => {
+ return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
+ },
},
builtin.TypeId.Array => |info| {
if (info.child == u8) {
diff --git a/std/meta/index.zig b/std/meta/index.zig
index 3f8ea762a6..652e2d39ec 100644
--- a/std/meta/index.zig
+++ b/std/meta/index.zig
@@ -463,13 +463,16 @@ pub fn eql(a: var, b: @typeOf(a)) bool {
builtin.TypeId.Pointer => {
const info = @typeInfo(T).Pointer;
switch (info.size) {
- builtin.TypeInfo.Pointer.Size.One, builtin.TypeInfo.Pointer.Size.Many => return a == b,
+ builtin.TypeInfo.Pointer.Size.One,
+ builtin.TypeInfo.Pointer.Size.Many,
+ builtin.TypeInfo.Pointer.Size.C,
+ => return a == b,
builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len,
}
},
builtin.TypeId.Optional => {
- if(a == null and b == null) return true;
- if(a == null or b == null) return false;
+ if (a == null and b == null) return true;
+ if (a == null or b == null) return false;
return eql(a.?, b.?);
},
else => return a == b,
diff --git a/std/testing.zig b/std/testing.zig
index ade6e8b0dd..f3ce69659d 100644
--- a/std/testing.zig
+++ b/std/testing.zig
@@ -69,7 +69,7 @@ pub fn expectEqual(expected: var, actual: var) void {
}
},
- builtin.TypeInfo.Pointer.Size.Slice => {
+ builtin.TypeInfo.Pointer.Size.Slice => {
if (actual.ptr != expected.ptr) {
std.debug.panic("expected slice ptr {}, found {}", expected.ptr, actual.ptr);
}
@@ -122,7 +122,6 @@ pub fn expectEqual(expected: var, actual: var) void {
}
}
},
-
}
}
diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig
index ce0ad795b4..dc185cc960 100644
--- a/test/stage1/behavior/type_info.zig
+++ b/test/stage1/behavior/type_info.zig
@@ -61,6 +61,21 @@ fn testUnknownLenPtr() void {
expect(u32_ptr_info.Pointer.child == f64);
}
+test "type info: C pointer type info" {
+ testCPtr();
+ comptime testCPtr();
+}
+
+fn testCPtr() void {
+ const ptr_info = @typeInfo([*c]align(4) const i8);
+ expect(TypeId(ptr_info) == TypeId.Pointer);
+ expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.C);
+ expect(ptr_info.Pointer.is_const);
+ expect(!ptr_info.Pointer.is_volatile);
+ expect(ptr_info.Pointer.alignment == 4);
+ expect(ptr_info.Pointer.child == i8);
+}
+
test "type info: slice type info" {
testSlice();
comptime testSlice();
--
cgit v1.2.3
From 59de24817e8538434f35a20a401f40c2f0231a9a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 14 Feb 2019 01:09:33 -0500
Subject: runtime safety check for casting null to pointer
see #1059
---
src/all_types.hpp | 3 +++
src/analyze.cpp | 9 +++++++++
src/analyze.hpp | 1 +
src/codegen.cpp | 19 +++++++++++++++++-
src/ir.cpp | 52 ++++++++++++++++++++++++-------------------------
test/runtime_safety.zig | 10 ++++++++++
6 files changed, 67 insertions(+), 27 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 230dba9a42..bafe316c3d 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1488,6 +1488,7 @@ enum PanicMsgId {
PanicMsgIdBadUnionField,
PanicMsgIdBadEnumValue,
PanicMsgIdFloatToInt,
+ PanicMsgIdPtrCastNull,
PanicMsgIdCount,
};
@@ -3001,12 +3002,14 @@ struct IrInstructionPtrCastSrc {
IrInstruction *dest_type;
IrInstruction *ptr;
+ bool safety_check_on;
};
struct IrInstructionPtrCastGen {
IrInstruction base;
IrInstruction *ptr;
+ bool safety_check_on;
};
struct IrInstructionBitCast {
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 900def52d4..6a8090a843 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -6902,3 +6902,12 @@ const char *container_string(ContainerKind kind) {
}
zig_unreachable();
}
+
+bool ptr_allows_addr_zero(ZigType *ptr_type) {
+ if (ptr_type->id == ZigTypeIdPointer) {
+ return ptr_type->data.pointer.allow_zero;
+ } else if (ptr_type->id == ZigTypeIdOptional) {
+ return true;
+ }
+ return false;
+}
diff --git a/src/analyze.hpp b/src/analyze.hpp
index c8141b02ff..50e841baa1 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -45,6 +45,7 @@ void find_libc_lib_path(CodeGen *g);
bool type_has_bits(ZigType *type_entry);
bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry);
+bool ptr_allows_addr_zero(ZigType *ptr_type);
ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 142e8174f5..dbb13ca885 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -950,6 +950,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
return buf_create_from_str("invalid enum value");
case PanicMsgIdFloatToInt:
return buf_create_from_str("integer part of floating point value out of bounds");
+ case PanicMsgIdPtrCastNull:
+ return buf_create_from_str("cast causes pointer to be null");
}
zig_unreachable();
}
@@ -3028,7 +3030,22 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable,
return nullptr;
}
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
- return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
+ LLVMValueRef result_ptr = LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
+ bool want_safety_check = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base);
+ if (!want_safety_check || ptr_allows_addr_zero(wanted_type))
+ return result_ptr;
+
+ LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(result_ptr));
+ LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntNE, result_ptr, zero, "");
+ LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastFail");
+ LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastOk");
+ LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
+
+ LLVMPositionBuilderAtEnd(g->builder, fail_block);
+ gen_safety_crash(g, PanicMsgIdPtrCastNull);
+
+ LLVMPositionBuilderAtEnd(g->builder, ok_block);
+ return result_ptr;
}
static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable,
diff --git a/src/ir.cpp b/src/ir.cpp
index f064adb128..dfbc36e02c 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -172,7 +172,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
ConstExprValue *out_val, ConstExprValue *ptr_val);
static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr,
- ZigType *dest_type, IrInstruction *dest_type_src);
+ ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on);
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed);
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs);
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
@@ -2202,12 +2202,13 @@ static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNo
}
static IrInstruction *ir_build_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
- IrInstruction *dest_type, IrInstruction *ptr)
+ IrInstruction *dest_type, IrInstruction *ptr, bool safety_check_on)
{
IrInstructionPtrCastSrc *instruction = ir_build_instruction(
irb, scope, source_node);
instruction->dest_type = dest_type;
instruction->ptr = ptr;
+ instruction->safety_check_on = safety_check_on;
ir_ref_instruction(dest_type, irb->current_basic_block);
ir_ref_instruction(ptr, irb->current_basic_block);
@@ -2216,12 +2217,13 @@ static IrInstruction *ir_build_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNod
}
static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction,
- ZigType *ptr_type, IrInstruction *ptr)
+ ZigType *ptr_type, IrInstruction *ptr, bool safety_check_on)
{
IrInstructionPtrCastGen *instruction = ir_build_instruction(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value.type = ptr_type;
instruction->ptr = ptr;
+ instruction->safety_check_on = safety_check_on;
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
@@ -4505,7 +4507,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
- IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value);
+ IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, ptr_cast, lval);
}
case BuiltinFnIdBitCast:
@@ -6740,7 +6742,8 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
// TODO relies on Zig not re-ordering fields
- IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst);
+ IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst,
+ false);
IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
@@ -6818,7 +6821,8 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode
get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
// TODO relies on Zig not re-ordering fields
- IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst);
+ IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst,
+ false);
IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
@@ -7363,7 +7367,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
u8_ptr_type = ir_build_const_type(irb, coro_scope, node,
get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false));
- IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr);
+ IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type,
+ coro_promise_ptr, false);
coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr);
coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node);
@@ -7387,7 +7392,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
ir_build_return(irb, coro_scope, node, undef);
ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block);
- IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr);
+ IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr,
+ false);
irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
@@ -7465,9 +7471,10 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8,
false, false, PtrLenUnknown, 0, 0, 0));
IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr);
- IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, result_ptr);
- IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
- irb->exec->coro_result_field_ptr);
+ IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
+ result_ptr, false);
+ IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node,
+ u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr, false);
IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node,
fn_entry->type_entry->data.fn.fn_type_id.return_type);
IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst);
@@ -7517,7 +7524,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node,
get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8,
false, false, PtrLenUnknown, 0, 0, 0));
- IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe);
+ IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
+ coro_mem_ptr_maybe, false);
IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false);
IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var);
IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr);
@@ -8644,15 +8652,6 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
return err_set_type;
}
-static bool ptr_allows_addr_zero(ZigType *ptr_type) {
- if (ptr_type->id == ZigTypeIdPointer) {
- return ptr_type->data.pointer.allow_zero;
- } else if (ptr_type->id == ZigTypeIdOptional) {
- return true;
- }
- return false;
-}
-
static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted_type,
ZigType *actual_type, AstNode *source_node, bool wanted_is_mutable)
{
@@ -11310,7 +11309,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
actual_type->data.pointer.host_int_bytes == dest_ptr_type->data.pointer.host_int_bytes &&
get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, dest_ptr_type))
{
- return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr);
+ return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr, true);
}
}
@@ -11352,7 +11351,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
actual_type->data.pointer.child_type, source_node,
!wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
{
- return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr);
+ return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr, true);
}
// cast from integer to C pointer
@@ -20616,7 +20615,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
}
static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr,
- ZigType *dest_type, IrInstruction *dest_type_src)
+ ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on)
{
Error err;
@@ -20685,7 +20684,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
return ira->codegen->invalid_instruction;
}
- IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr);
+ IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on);
if (type_has_bits(dest_type) && !type_has_bits(src_type)) {
ErrorMsg *msg = ir_add_error(ira, source_instr,
@@ -20722,7 +20721,8 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct
if (type_is_invalid(src_type))
return ira->codegen->invalid_instruction;
- return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value);
+ return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value,
+ instruction->safety_check_on);
}
static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *val, size_t len) {
diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig
index 821328b7a6..12cac64b3a 100644
--- a/test/runtime_safety.zig
+++ b/test/runtime_safety.zig
@@ -1,6 +1,16 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompareOutputContext) void {
+ cases.addRuntimeSafety("pointer casting null to non-optional pointer",
+ \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
+ \\ @import("std").os.exit(126);
+ \\}
+ \\pub fn main() void {
+ \\ var c_ptr: [*c]u8 = 0;
+ \\ var zig_ptr: *u8 = c_ptr;
+ \\}
+ );
+
cases.addRuntimeSafety("@intToEnum - no matching tag value",
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
--
cgit v1.2.3
From df87044fd6588452755014d5909e0db1b776deb2 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 14 Feb 2019 16:10:12 -0500
Subject: omit nonnull attribute for C pointers
See #1059
---
src/analyze.cpp | 4 ++++
src/analyze.hpp | 1 +
src/codegen.cpp | 19 ++++++++++++++++---
src/target.cpp | 4 ++++
src/target.hpp | 1 +
5 files changed, 26 insertions(+), 3 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 90ce3d3371..55deafb3a8 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -4100,6 +4100,10 @@ ZigType *get_codegen_ptr_type(ZigType *type) {
return ty;
}
+bool type_is_nonnull_ptr(ZigType *type) {
+ return type_is_codegen_pointer(type) && !ptr_allows_addr_zero(type);
+}
+
bool type_is_codegen_pointer(ZigType *type) {
return get_codegen_ptr_type(type) == type;
}
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 50e841baa1..845fb62534 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -46,6 +46,7 @@ void find_libc_lib_path(CodeGen *g);
bool type_has_bits(ZigType *type_entry);
bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry);
bool ptr_allows_addr_zero(ZigType *ptr_type);
+bool type_is_nonnull_ptr(ZigType *type);
ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index dbb13ca885..bae9abe06d 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -617,9 +617,10 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
unsigned init_gen_i = 0;
if (!type_has_bits(return_type)) {
// nothing to do
- } else if (type_is_codegen_pointer(return_type)) {
+ } else if (type_is_nonnull_ptr(return_type)) {
addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");
} else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
+ // Sret pointers must not be address 0
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull");
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
if (cc_want_sret_attr(cc)) {
@@ -637,6 +638,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
if (err_ret_trace_arg_index != UINT32_MAX) {
+ // Error return trace memory is in the stack, which is impossible to be at address 0
+ // on any architecture.
addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
}
@@ -1246,6 +1249,8 @@ static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) {
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
add_uwtable_attr(g, fn_val);
+ // Error return trace memory is in the stack, which is impossible to be at address 0
+ // on any architecture.
addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
if (g->build_mode == BuildModeDebug) {
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
@@ -1320,9 +1325,13 @@ static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) {
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
add_uwtable_attr(g, fn_val);
+ // Error return trace memory is in the stack, which is impossible to be at address 0
+ // on any architecture.
addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
addLLVMArgAttr(fn_val, (unsigned)0, "noalias");
addLLVMArgAttr(fn_val, (unsigned)0, "writeonly");
+ // Error return trace memory is in the stack, which is impossible to be at address 0
+ // on any architecture.
addLLVMArgAttr(fn_val, (unsigned)1, "nonnull");
addLLVMArgAttr(fn_val, (unsigned)1, "noalias");
addLLVMArgAttr(fn_val, (unsigned)1, "readonly");
@@ -1450,6 +1459,8 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) {
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
add_uwtable_attr(g, fn_val);
+ // Error return trace memory is in the stack, which is impossible to be at address 0
+ // on any architecture.
addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
if (g->build_mode == BuildModeDebug) {
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
@@ -2051,7 +2062,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
case FnWalkIdAttrs: {
ZigType *ptr_type = get_codegen_ptr_type(ty);
if (ptr_type != nullptr) {
- if (ty->id != ZigTypeIdOptional) {
+ if (type_is_nonnull_ptr(ty)) {
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull");
}
if (ptr_type->data.pointer.is_const) {
@@ -2095,6 +2106,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
assert(handle_is_ptr(ty));
switch (fn_walk->id) {
case FnWalkIdAttrs:
+ // arrays passed to C ABI functions may not be at address 0
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull");
addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty));
fn_walk->data.attrs.gen_i += 1;
@@ -2134,6 +2146,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
case FnWalkIdAttrs:
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval");
addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty));
+ // Byvalue parameters must not have address 0
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull");
fn_walk->data.attrs.gen_i += 1;
break;
@@ -2266,7 +2279,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) {
if ((param_type->id == ZigTypeIdPointer && param_type->data.pointer.is_const) || is_byval) {
addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "readonly");
}
- if (param_type->id == ZigTypeIdPointer) {
+ if (type_is_nonnull_ptr(param_type)) {
addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "nonnull");
}
break;
diff --git a/src/target.cpp b/src/target.cpp
index 6fea79518c..b1434c6871 100644
--- a/src/target.cpp
+++ b/src/target.cpp
@@ -807,6 +807,10 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
zig_unreachable();
}
+bool target_allows_addr_zero(const ZigTarget *target) {
+ return target->os == OsFreestanding;
+}
+
const char *target_o_file_ext(ZigTarget *target) {
if (target->env_type == ZigLLVM_MSVC || target->os == OsWindows || target->os == OsUefi) {
return ".obj";
diff --git a/src/target.hpp b/src/target.hpp
index a87b12351a..99d1cadf56 100644
--- a/src/target.hpp
+++ b/src/target.hpp
@@ -135,5 +135,6 @@ bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target
ZigLLVM_OSType get_llvm_os_type(Os os_type);
bool target_is_arm(const ZigTarget *target);
+bool target_allows_addr_zero(const ZigTarget *target);
#endif
--
cgit v1.2.3
From a05e224150a5a4bcad5ab1b399b43db8a0e28104 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 15 Feb 2019 19:19:28 -0500
Subject: typecheck the panic function
this adds the prototype of panic to @import("builtin")
and then uses it to do an implicit cast of the panic
function to this prototype, rather than redoing all the
implicit cast logic.
closes #1894
closes #1895
---
src/all_types.hpp | 1 +
src/analyze.cpp | 61 ++++++++++++++++++-------------------------------
src/analyze.hpp | 1 +
src/codegen.cpp | 4 ++++
src/ir.cpp | 15 +++++++-----
src/ir.hpp | 2 +-
test/compile_errors.zig | 14 ++++++++++--
7 files changed, 50 insertions(+), 48 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index bafe316c3d..6fbd987b9e 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1758,6 +1758,7 @@ struct CodeGen {
ZigFn *cur_fn;
ZigFn *main_fn;
ZigFn *panic_fn;
+ TldFn *panic_tld_fn;
AstNode *root_export_decl;
CacheHash cache_hash;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 7949493586..12e245bd72 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -1351,7 +1351,7 @@ static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *no
size_t backward_branch_count = 0;
return ir_eval_const_value(g, scope, node, type_entry,
&backward_branch_count, default_backward_branch_quota,
- nullptr, nullptr, node, type_name, nullptr);
+ nullptr, nullptr, node, type_name, nullptr, nullptr);
}
ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
@@ -3247,36 +3247,19 @@ static bool scope_is_root_decls(Scope *scope) {
zig_unreachable();
}
-static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, ZigType *fn_type) {
- add_node_error(g, proto_node,
- buf_sprintf("expected 'fn([]const u8, ?*builtin.StackTrace) noreturn', found '%s'",
- buf_ptr(&fn_type->name)));
-}
-
-static void typecheck_panic_fn(CodeGen *g, ZigFn *panic_fn) {
- AstNode *proto_node = panic_fn->proto_node;
- assert(proto_node->type == NodeTypeFnProto);
- ZigType *fn_type = panic_fn->type_entry;
- FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
- if (fn_type_id->param_count != 2) {
- return wrong_panic_prototype(g, proto_node, fn_type);
- }
- ZigType *const_u8_ptr = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
- PtrLenUnknown, 0, 0, 0);
- ZigType *const_u8_slice = get_slice_type(g, const_u8_ptr);
- if (fn_type_id->param_info[0].type != const_u8_slice) {
- return wrong_panic_prototype(g, proto_node, fn_type);
- }
+void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn) {
+ ConstExprValue *panic_fn_type_val = get_builtin_value(g, "PanicFn");
+ assert(panic_fn_type_val != nullptr);
+ assert(panic_fn_type_val->type->id == ZigTypeIdMetaType);
+ ZigType *panic_fn_type = panic_fn_type_val->data.x_type;
- ZigType *optional_ptr_to_stack_trace_type = get_optional_type(g, get_ptr_to_stack_trace_type(g));
- if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) {
- return wrong_panic_prototype(g, proto_node, fn_type);
- }
+ AstNode *fake_decl = allocate(1);
+ *fake_decl = *panic_fn->proto_node;
+ fake_decl->type = NodeTypeSymbol;
+ fake_decl->data.symbol_expr.symbol = &panic_fn->symbol_name;
- ZigType *actual_return_type = fn_type_id->return_type;
- if (actual_return_type != g->builtin_types.entry_unreachable) {
- return wrong_panic_prototype(g, proto_node, fn_type);
- }
+ // call this for the side effects of casting to panic_fn_type
+ analyze_const_value(g, tld_fn->base.parent_scope, fake_decl, panic_fn_type, nullptr);
}
ZigType *get_test_fn_type(CodeGen *g) {
@@ -3371,18 +3354,18 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
if (!fn_table_entry->type_entry->data.fn.is_generic) {
if (fn_def_node)
g->fn_defs.append(fn_table_entry);
+ }
- if (scope_is_root_decls(tld_fn->base.parent_scope) &&
- (import == g->root_import || import->package == g->panic_package))
+ if (scope_is_root_decls(tld_fn->base.parent_scope) &&
+ (import == g->root_import || import->package == g->panic_package))
+ {
+ if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {
+ g->main_fn = fn_table_entry;
+ } else if ((import->package == g->panic_package || g->have_pub_panic) &&
+ buf_eql_str(&fn_table_entry->symbol_name, "panic"))
{
- if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {
- g->main_fn = fn_table_entry;
- } else if ((import->package == g->panic_package || g->have_pub_panic) &&
- buf_eql_str(&fn_table_entry->symbol_name, "panic"))
- {
- g->panic_fn = fn_table_entry;
- typecheck_panic_fn(g, fn_table_entry);
- }
+ g->panic_fn = fn_table_entry;
+ g->panic_tld_fn = tld_fn;
}
}
} else if (source_node->type == NodeTypeTestDecl) {
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 7ded651e95..956ef47309 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -239,4 +239,5 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry);
Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
ConstExprValue *const_val, ZigType *wanted_type);
+void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn);
#endif
diff --git a/src/codegen.cpp b/src/codegen.cpp
index d2662b10d2..d2b2836b0c 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -7144,6 +7144,8 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
" instruction_addresses: []usize,\n"
"};\n\n");
+ buf_append_str(contents, "pub const PanicFn = fn([]const u8, ?*StackTrace) noreturn;\n\n");
+
const char *cur_os = nullptr;
{
buf_appendf(contents, "pub const Os = enum {\n");
@@ -7913,6 +7915,8 @@ static void gen_root_source(CodeGen *g) {
}
}
+ typecheck_panic_fn(g, g->panic_tld_fn, g->panic_fn);
+
report_errors_and_maybe_exit(g);
}
diff --git a/src/ir.cpp b/src/ir.cpp
index 92cdd8c891..0fcbb60fe8 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -9949,7 +9949,7 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un
ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota,
ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name,
- IrExecutable *parent_exec)
+ IrExecutable *parent_exec, AstNode *expected_type_source_node)
{
if (expected_type != nullptr && type_is_invalid(expected_type))
return &codegen->invalid_instruction->value;
@@ -9985,7 +9985,7 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod
analyzed_executable->backward_branch_count = backward_branch_count;
analyzed_executable->backward_branch_quota = backward_branch_quota;
analyzed_executable->begin_scope = scope;
- ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node);
+ ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, expected_type_source_node);
if (type_is_invalid(result_type))
return &codegen->invalid_instruction->value;
@@ -10863,10 +10863,13 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
}
break;
}
+ case ConstCastResultIdFnIsGeneric:
+ add_error_note(ira->codegen, parent_msg, source_node,
+ buf_sprintf("only one of the functions is generic"));
+ break;
case ConstCastResultIdFnAlign: // TODO
case ConstCastResultIdFnCC: // TODO
case ConstCastResultIdFnVarArgs: // TODO
- case ConstCastResultIdFnIsGeneric: // TODO
case ConstCastResultIdFnReturnType: // TODO
case ConstCastResultIdFnArgCount: // TODO
case ConstCastResultIdFnGenericArgCount: // TODO
@@ -13856,7 +13859,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call
AstNode *body_node = fn_entry->body_node;
result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type,
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
- nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec);
+ nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec, return_type_node);
if (inferred_err_set_type != nullptr) {
inferred_err_set_type->data.error_set.infer_fn = nullptr;
@@ -14052,7 +14055,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call
ConstExprValue *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope,
fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen),
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota,
- nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec);
+ nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec, nullptr);
IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb,
impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr);
const_instruction->base.value = *align_result;
@@ -18464,7 +18467,7 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct
ZigType *void_type = ira->codegen->builtin_types.entry_void;
ConstExprValue *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type,
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr,
- &cimport_scope->buf, block_node, nullptr, nullptr);
+ &cimport_scope->buf, block_node, nullptr, nullptr, nullptr);
if (type_is_invalid(cimport_result->type))
return ira->codegen->invalid_instruction;
diff --git a/src/ir.hpp b/src/ir.hpp
index 0a7c614812..0b85ad2c55 100644
--- a/src/ir.hpp
+++ b/src/ir.hpp
@@ -16,7 +16,7 @@ bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry);
ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota,
ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name,
- IrExecutable *parent_exec);
+ IrExecutable *parent_exec, AstNode *expected_type_source_node);
ZigType *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable,
ZigType *expected_type, AstNode *expected_type_source_node);
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index e7108cb36a..9ef4af4162 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -346,13 +346,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
);
cases.add(
- "Panic declared with wrong type signature in tests",
+ "wrong panic signature, runtime function",
\\test "" {}
\\
\\pub fn panic() void {}
\\
,
- ".tmp_source.zig:3:5: error: expected 'fn([]const u8, ?*builtin.StackTrace) noreturn', found 'fn() void'",
+ ".tmp_source.zig:3:5: error: expected type 'fn([]const u8, ?*StackTrace) noreturn', found 'fn() void'",
+ );
+
+ cases.add(
+ "wrong panic signature, generic function",
+ \\pub fn panic(comptime msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
+ \\ while (true) {}
+ \\}
+ ,
+ ".tmp_source.zig:1:5: error: expected type 'fn([]const u8, ?*StackTrace) noreturn', found 'fn([]const u8,var)var'",
+ ".tmp_source.zig:1:5: note: only one of the functions is generic",
);
cases.add(
--
cgit v1.2.3