From aa3e0ff454d06407b4ee347c1cd0c1e09444c52c Mon Sep 17 00:00:00 2001
From: Luuk de Gram
Date: Wed, 3 Feb 2021 22:15:18 +0100
Subject: [PATCH 01/20] Create type declarations for extern functions and write
the 'import' section
---
lib/std/wasm.zig | 14 ++++++++++
src/codegen/wasm.zig | 28 +++++++++++++------
src/link/Wasm.zig | 66 +++++++++++++++++++++++++++++++++++++++-----
3 files changed, 93 insertions(+), 15 deletions(-)
diff --git a/lib/std/wasm.zig b/lib/std/wasm.zig
index aa087c89c9..c59117d65b 100644
--- a/lib/std/wasm.zig
+++ b/lib/std/wasm.zig
@@ -253,6 +253,20 @@ pub fn section(val: Section) u8 {
return @enumToInt(val);
}
+/// The kind of the type when importing or exporting to/from the host environment
+/// https://webassembly.github.io/spec/core/syntax/modules.html
+pub const ExternalKind = enum(u8) {
+ function,
+ table,
+ memory,
+ global,
+};
+
+/// Returns the integer value of a given `ExternalKind`
+pub fn kind(val: ExternalKind) u8 {
+ return @enumToInt(val);
+}
+
// types
pub const element_type: u8 = 0x70;
pub const function_type: u8 = 0x60;
diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig
index abc411b551..5be26de7cb 100644
--- a/src/codegen/wasm.zig
+++ b/src/codegen/wasm.zig
@@ -163,13 +163,18 @@ pub const Context = struct {
try self.genFunctype();
const writer = self.code.writer();
- // Reserve space to write the size after generating the code as well as space for locals count
- try self.code.resize(10);
-
// Write instructions
// TODO: check for and handle death of instructions
const tv = self.decl.typed_value.most_recent.typed_value;
- const mod_fn = tv.val.castTag(.function).?.data;
+ const mod_fn = blk: {
+ if (tv.val.castTag(.function)) |func| break :blk func.data;
+ if (tv.val.castTag(.extern_fn)) |ext_fn| return; // don't need codegen for extern functions
+ return self.fail(self.decl.src(), "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()});
+ };
+
+ // Reserve space to write the size after generating the code as well as space for locals count
+ try self.code.resize(10);
+
try self.genBody(mod_fn.body);
// finally, write our local types at the 'offset' position
@@ -239,9 +244,16 @@ pub const Context = struct {
fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue {
const func_inst = inst.func.castTag(.constant).?;
- const func = func_inst.val.castTag(.function).?.data;
- const target = func.owner_decl;
- const target_ty = target.typed_value.most_recent.typed_value.ty;
+ const func_val = inst.func.value().?;
+
+ const target = blk: {
+ if (func_val.castTag(.function)) |func| {
+ break :blk func.data.owner_decl;
+ } else if (func_val.castTag(.extern_fn)) |ext_fn| {
+ break :blk ext_fn.data;
+ }
+ return self.fail(inst.base.src, "Expected a function, but instead found type '{s}'", .{func_val.tag()});
+ };
for (inst.args) |arg| {
const arg_val = self.resolveInst(arg);
@@ -495,7 +507,7 @@ pub const Context = struct {
}
fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue {
- // of operand has codegen bits we should break with a value
+ // if operand has codegen bits we should break with a value
if (br.operand.ty.hasCodeGenBits()) {
const operand = self.resolveInst(br.operand);
try self.emitWValue(operand);
diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig
index 547ab2a012..9660ff0a43 100644
--- a/src/link/Wasm.zig
+++ b/src/link/Wasm.zig
@@ -85,8 +85,6 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
const typed_value = decl.typed_value.most_recent.typed_value;
if (typed_value.ty.zigTypeTag() != .Fn)
return error.TODOImplementNonFnDeclsForWasm;
- if (typed_value.val.tag() == .extern_fn)
- return error.TODOImplementExternFnDeclsForWasm;
if (decl.fn_link.wasm) |*fn_data| {
fn_data.functype.items.len = 0;
@@ -184,17 +182,63 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
);
}
+ // Import section
+ {
+ // TODO: implement non-functions imports
+ const header_offset = try reserveVecSectionHeader(file);
+ const writer = file.writer();
+ var count: u32 = 0;
+ for (self.funcs.items) |decl, typeidx| {
+ if (decl.typed_value.most_recent.typed_value.val.tag() != .extern_fn) {
+ continue;
+ }
+
+ // TODO: can we set/save the module name somewhere?
+ // For now, emit "env" like LLVM does
+ const module_name = "env";
+ try leb.writeULEB128(writer, @intCast(u32, module_name.len));
+ try writer.writeAll(module_name);
+
+ // wasm requires the length of the import name and doesn't require a null-termination
+ const decl_len = mem.len(decl.name);
+ try leb.writeULEB128(writer, @intCast(u32, decl_len));
+ try writer.writeAll(decl.name[0..decl_len]);
+
+ // emit kind and the function type
+ try writer.writeByte(wasm.kind(.function));
+ try leb.writeULEB128(writer, @intCast(u32, typeidx));
+
+ count += 1;
+ }
+
+ try writeVecSectionHeader(
+ file,
+ header_offset,
+ .import,
+ @intCast(u32, (try file.getPos()) - header_offset - header_size),
+ count,
+ );
+ }
+
// Function section
{
const header_offset = try reserveVecSectionHeader(file);
const writer = file.writer();
- for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx));
+ var count: u32 = 0;
+ for (self.funcs.items) |decl, typeidx| {
+ // Extern functions only have a type, so skip the function signature section
+ if (decl.typed_value.most_recent.typed_value.val.tag() != .function) {
+ continue;
+ }
+ try leb.writeULEB128(writer, @intCast(u32, typeidx));
+ count += 1;
+ }
try writeVecSectionHeader(
file,
header_offset,
.function,
@intCast(u32, (try file.getPos()) - header_offset - header_size),
- @intCast(u32, self.funcs.items.len),
+ count,
);
}
@@ -214,7 +258,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
// Type of the export
try writer.writeByte(0x00);
// Exported function index
- try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?);
+ try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).? + 1);
},
else => return error.TODOImplementNonFnDeclsForWasm,
}
@@ -235,7 +279,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
{
const header_offset = try reserveVecSectionHeader(file);
const writer = file.writer();
+ var count: u32 = 0;
for (self.funcs.items) |decl| {
+ // Do not emit any code for extern functions
+ if (decl.typed_value.most_recent.typed_value.val.tag() != .function) {
+ std.debug.print("Skipping decl: {s}\n", .{decl.name});
+ continue;
+ }
const fn_data = &decl.fn_link.wasm.?;
// Write the already generated code to the file, inserting
@@ -247,18 +297,20 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
// Use a fixed width here to make calculating the code size
// in codegen.wasm.gen() simpler.
var buf: [5]u8 = undefined;
- leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?);
+ std.debug.print("idx_ref: {s} - {d}\n", .{ idx_ref.decl.name, self.getFuncidx(idx_ref.decl).? });
+ leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).? - 1);
try writer.writeAll(&buf);
}
try writer.writeAll(fn_data.code.items[current..]);
+ count += 1;
}
try writeVecSectionHeader(
file,
header_offset,
.code,
@intCast(u32, (try file.getPos()) - header_offset - header_size),
- @intCast(u32, self.funcs.items.len),
+ count,
);
}
}
From 36df6a008fb009fd8fd1d3362fc850cf97723dcd Mon Sep 17 00:00:00 2001
From: Luuk de Gram
Date: Thu, 4 Feb 2021 21:08:19 +0100
Subject: [PATCH 02/20] Ensure function indices are correct and fix a memory
leak
---
lib/std/wasm.zig | 2 +-
src/codegen/wasm.zig | 2 +-
src/link/Wasm.zig | 118 ++++++++++++++++++++++++++-----------------
3 files changed, 73 insertions(+), 49 deletions(-)
diff --git a/lib/std/wasm.zig b/lib/std/wasm.zig
index c59117d65b..a04378d283 100644
--- a/lib/std/wasm.zig
+++ b/lib/std/wasm.zig
@@ -263,7 +263,7 @@ pub const ExternalKind = enum(u8) {
};
/// Returns the integer value of a given `ExternalKind`
-pub fn kind(val: ExternalKind) u8 {
+pub fn externalKind(val: ExternalKind) u8 {
return @enumToInt(val);
}
diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig
index 5be26de7cb..34e0b2f9b5 100644
--- a/src/codegen/wasm.zig
+++ b/src/codegen/wasm.zig
@@ -161,7 +161,6 @@ pub const Context = struct {
pub fn gen(self: *Context) InnerError!void {
assert(self.code.items.len == 0);
try self.genFunctype();
- const writer = self.code.writer();
// Write instructions
// TODO: check for and handle death of instructions
@@ -194,6 +193,7 @@ pub const Context = struct {
}
}
+ const writer = self.code.writer();
try writer.writeByte(wasm.opcode(.end));
// Fill in the size of the generated code to the reserved space at the
diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig
index 9660ff0a43..bd89ce3345 100644
--- a/src/link/Wasm.zig
+++ b/src/link/Wasm.zig
@@ -33,9 +33,18 @@ base: link.File,
/// List of all function Decls to be written to the output file. The index of
/// each Decl in this list at the time of writing the binary is used as the
-/// function index.
+/// function index. In the event where ext_funcs' size is not 0, the index of
+/// each function is added on top of the ext_funcs' length.
/// TODO: can/should we access some data structure in Module directly?
funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
+/// List of all extern function Decls to be written to the `import` section of the
+/// wasm binary. The positin in the list defines the function index
+ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
+/// When importing objects from the host environment, a name must be supplied.
+/// LLVM uses "env" by default when none is given. This would be a good default for Zig
+/// to support existing code.
+/// TODO: Allow setting this through a flag?
+host_name: []const u8 = "env",
pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
assert(options.object_format == .wasm);
@@ -76,7 +85,13 @@ pub fn deinit(self: *Wasm) void {
decl.fn_link.wasm.?.code.deinit(self.base.allocator);
decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
}
+ for (self.ext_funcs.items) |decl| {
+ decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
+ decl.fn_link.wasm.?.code.deinit(self.base.allocator);
+ decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
+ }
self.funcs.deinit(self.base.allocator);
+ self.ext_funcs.deinit(self.base.allocator);
}
// Generate code for the Decl, storing it in memory to be later written to
@@ -92,7 +107,12 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
fn_data.idx_refs.items.len = 0;
} else {
decl.fn_link.wasm = .{};
- try self.funcs.append(self.base.allocator, decl);
+ // dependent on function type, appends it to the correct list
+ switch (decl.typed_value.most_recent.typed_value.val.tag()) {
+ .function => try self.funcs.append(self.base.allocator, decl),
+ .extern_fn => try self.ext_funcs.append(self.base.allocator, decl),
+ else => return error.TODOImplementNonFnDeclsForWasm,
+ }
}
const fn_data = &decl.fn_link.wasm.?;
@@ -141,7 +161,12 @@ pub fn updateDeclExports(
pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
// TODO: remove this assert when non-function Decls are implemented
assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn);
- _ = self.funcs.swapRemove(self.getFuncidx(decl).?);
+ const func_idx = self.getFuncidx(decl).?;
+ switch (decl.typed_value.most_recent.typed_value.val.tag()) {
+ .function => _ = self.funcs.swapRemove(func_idx),
+ .extern_fn => _ = self.ext_funcs.swapRemove(func_idx),
+ else => unreachable,
+ }
decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
decl.fn_link.wasm.?.code.deinit(self.base.allocator);
decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
@@ -170,15 +195,18 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
// Type section
{
const header_offset = try reserveVecSectionHeader(file);
- for (self.funcs.items) |decl| {
- try file.writeAll(decl.fn_link.wasm.?.functype.items);
- }
+
+ // extern functions are defined in the wasm binary first through the `import`
+ // section, so define their func types first
+ for (self.ext_funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items);
+ for (self.funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items);
+
try writeVecSectionHeader(
file,
header_offset,
.type,
@intCast(u32, (try file.getPos()) - header_offset - header_size),
- @intCast(u32, self.funcs.items.len),
+ @intCast(u32, self.ext_funcs.items.len + self.funcs.items.len),
);
}
@@ -187,28 +215,18 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
// TODO: implement non-functions imports
const header_offset = try reserveVecSectionHeader(file);
const writer = file.writer();
- var count: u32 = 0;
- for (self.funcs.items) |decl, typeidx| {
- if (decl.typed_value.most_recent.typed_value.val.tag() != .extern_fn) {
- continue;
- }
+ for (self.ext_funcs.items) |decl, typeidx| {
+ try leb.writeULEB128(writer, @intCast(u32, self.host_name.len));
+ try writer.writeAll(self.host_name);
- // TODO: can we set/save the module name somewhere?
- // For now, emit "env" like LLVM does
- const module_name = "env";
- try leb.writeULEB128(writer, @intCast(u32, module_name.len));
- try writer.writeAll(module_name);
-
- // wasm requires the length of the import name and doesn't require a null-termination
+ // wasm requires the length of the import name with no null-termination
const decl_len = mem.len(decl.name);
try leb.writeULEB128(writer, @intCast(u32, decl_len));
try writer.writeAll(decl.name[0..decl_len]);
// emit kind and the function type
- try writer.writeByte(wasm.kind(.function));
+ try writer.writeByte(wasm.externalKind(.function));
try leb.writeULEB128(writer, @intCast(u32, typeidx));
-
- count += 1;
}
try writeVecSectionHeader(
@@ -216,7 +234,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
header_offset,
.import,
@intCast(u32, (try file.getPos()) - header_offset - header_size),
- count,
+ @intCast(u32, self.ext_funcs.items.len),
);
}
@@ -224,21 +242,17 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
{
const header_offset = try reserveVecSectionHeader(file);
const writer = file.writer();
- var count: u32 = 0;
- for (self.funcs.items) |decl, typeidx| {
- // Extern functions only have a type, so skip the function signature section
- if (decl.typed_value.most_recent.typed_value.val.tag() != .function) {
- continue;
- }
- try leb.writeULEB128(writer, @intCast(u32, typeidx));
- count += 1;
+ for (self.funcs.items) |_, typeidx| {
+ const func_idx = @intCast(u32, self.getFuncIdxOffset() + typeidx);
+ try leb.writeULEB128(writer, func_idx);
}
+
try writeVecSectionHeader(
file,
header_offset,
.function,
@intCast(u32, (try file.getPos()) - header_offset - header_size),
- count,
+ @intCast(u32, self.funcs.items.len),
);
}
@@ -256,9 +270,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
switch (exprt.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
.Fn => {
// Type of the export
- try writer.writeByte(0x00);
+ try writer.writeByte(wasm.externalKind(.function));
// Exported function index
- try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).? + 1);
+ try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?);
},
else => return error.TODOImplementNonFnDeclsForWasm,
}
@@ -279,13 +293,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
{
const header_offset = try reserveVecSectionHeader(file);
const writer = file.writer();
- var count: u32 = 0;
for (self.funcs.items) |decl| {
- // Do not emit any code for extern functions
- if (decl.typed_value.most_recent.typed_value.val.tag() != .function) {
- std.debug.print("Skipping decl: {s}\n", .{decl.name});
- continue;
- }
const fn_data = &decl.fn_link.wasm.?;
// Write the already generated code to the file, inserting
@@ -297,20 +305,18 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
// Use a fixed width here to make calculating the code size
// in codegen.wasm.gen() simpler.
var buf: [5]u8 = undefined;
- std.debug.print("idx_ref: {s} - {d}\n", .{ idx_ref.decl.name, self.getFuncidx(idx_ref.decl).? });
- leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).? - 1);
+ leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?);
try writer.writeAll(&buf);
}
try writer.writeAll(fn_data.code.items[current..]);
- count += 1;
}
try writeVecSectionHeader(
file,
header_offset,
.code,
@intCast(u32, (try file.getPos()) - header_offset - header_size),
- count,
+ @intCast(u32, self.funcs.items.len),
);
}
}
@@ -575,13 +581,31 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
}
/// Get the current index of a given Decl in the function list
-/// TODO: we could maintain a hash map to potentially make this
+/// This will correctly provide the index, regardless whether the function is extern or not
+/// TODO: we could maintain a hash map to potentially make this simpler
fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 {
- return for (self.funcs.items) |func, idx| {
- if (func == decl) break @intCast(u32, idx);
+ var offset: u32 = 0;
+ const slice = switch (decl.typed_value.most_recent.typed_value.val.tag()) {
+ .function => blk: {
+ // when the target is a regular function, we have to calculate
+ // the offset of where the index starts
+ offset += self.getFuncIdxOffset();
+ break :blk self.funcs.items;
+ },
+ .extern_fn => self.ext_funcs.items,
+ else => return null,
+ };
+ return for (slice) |func, idx| {
+ if (func == decl) break @intCast(u32, offset + idx);
} else null;
}
+/// Based on the size of `ext_funcs` returns the
+/// offset of the function indices
+fn getFuncIdxOffset(self: Wasm) u32 {
+ return @intCast(u32, self.ext_funcs.items.len);
+}
+
fn reserveVecSectionHeader(file: fs.File) !u64 {
// section id + fixed leb contents size + fixed leb vector length
const header_size = 1 + 5 + 5;
From 9270aae071a4ee840193afe1162b24945cbd6d9e Mon Sep 17 00:00:00 2001
From: Tadeo Kondrak
Date: Fri, 12 Feb 2021 13:40:44 -0700
Subject: [PATCH 03/20] stage2: fix zero-sized function parameters (#7998)
---
src/codegen.zig | 25 +++++++++++++++----------
test/stage2/test.zig | 22 ++++++++++++++++++++++
2 files changed, 37 insertions(+), 10 deletions(-)
diff --git a/src/codegen.zig b/src/codegen.zig
index 9771386403..d81ad1faf5 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -3705,17 +3705,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
for (param_types) |ty, i| {
switch (ty.zigTypeTag()) {
.Bool, .Int => {
- const param_size = @intCast(u32, ty.abiSize(self.target.*));
- if (next_int_reg >= c_abi_int_param_regs.len) {
- result.args[i] = .{ .stack_offset = next_stack_offset };
- next_stack_offset += param_size;
+ if (!ty.hasCodeGenBits()) {
+ assert(cc != .C);
+ result.args[i] = .{ .none = {} };
} else {
- const aliased_reg = registerAlias(
- c_abi_int_param_regs[next_int_reg],
- param_size,
- );
- result.args[i] = .{ .register = aliased_reg };
- next_int_reg += 1;
+ const param_size = @intCast(u32, ty.abiSize(self.target.*));
+ if (next_int_reg >= c_abi_int_param_regs.len) {
+ result.args[i] = .{ .stack_offset = next_stack_offset };
+ next_stack_offset += param_size;
+ } else {
+ const aliased_reg = registerAlias(
+ c_abi_int_param_regs[next_int_reg],
+ param_size,
+ );
+ result.args[i] = .{ .register = aliased_reg };
+ next_int_reg += 1;
+ }
}
},
else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}),
diff --git a/test/stage2/test.zig b/test/stage2/test.zig
index 486edeb864..b5de03524f 100644
--- a/test/stage2/test.zig
+++ b/test/stage2/test.zig
@@ -1393,4 +1393,26 @@ pub fn addCases(ctx: *TestContext) !void {
"",
);
}
+
+ {
+ var case = ctx.exe("passing u0 to function", linux_x64);
+ case.addCompareOutput(
+ \\export fn _start() noreturn {
+ \\ doNothing(0);
+ \\ exit();
+ \\}
+ \\fn doNothing(arg: u0) void {}
+ \\fn exit() noreturn {
+ \\ asm volatile ("syscall"
+ \\ :
+ \\ : [number] "{rax}" (231),
+ \\ [arg1] "{rdi}" (0)
+ \\ : "rcx", "r11", "memory"
+ \\ );
+ \\ unreachable;
+ \\}
+ ,
+ "",
+ );
+ }
}
From 68e7726478f89ca27127e68b79112c12ac7415f7 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 16 Feb 2021 11:01:17 -0700
Subject: [PATCH 04/20] std.fs.net.Stream: add writev and writevAll
I noticed that the write function does not properly use non-blocking
I/O. This file needs to be reworked for evented I/O to properly take
advantage of non-blocking writes to network sockets.
---
lib/std/fs/file.zig | 2 ++
lib/std/net.zig | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 39 insertions(+)
diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig
index 5138555723..baea3b4e7f 100644
--- a/lib/std/fs/file.zig
+++ b/lib/std/fs/file.zig
@@ -587,6 +587,7 @@ pub const File = struct {
}
/// See https://github.com/ziglang/zig/issues/7699
+ /// See equivalent function: `std.net.Stream.writev`.
pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize {
if (is_windows) {
// TODO improve this to use WriteFileScatter
@@ -605,6 +606,7 @@ pub const File = struct {
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
/// order to handle partial writes from the underlying OS layer.
/// See https://github.com/ziglang/zig/issues/7699
+ /// See equivalent function: `std.net.Stream.writevAll`.
pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void {
if (iovecs.len == 0) return;
diff --git a/lib/std/net.zig b/lib/std/net.zig
index 28ae2b9857..2d62a27cd9 100644
--- a/lib/std/net.zig
+++ b/lib/std/net.zig
@@ -1621,6 +1621,9 @@ pub const Stream = struct {
}
}
+ /// TODO in evented I/O mode, this implementation incorrectly uses the event loop's
+ /// file system thread instead of non-blocking. It needs to be reworked to properly
+ /// use non-blocking I/O.
pub fn write(self: Stream, buffer: []const u8) WriteError!usize {
if (std.Target.current.os.tag == .windows) {
return os.windows.WriteFile(self.handle, buffer, null, io.default_mode);
@@ -1632,6 +1635,40 @@ pub const Stream = struct {
return os.write(self.handle, buffer);
}
}
+
+ /// See https://github.com/ziglang/zig/issues/7699
+ /// See equivalent function: `std.fs.File.writev`.
+ pub fn writev(self: Stream, iovecs: []const os.iovec_const) WriteError!usize {
+ if (std.io.is_async) {
+ // TODO improve to actually take advantage of writev syscall, if available.
+ if (iovecs.len == 0) return 0;
+ const first_buffer = iovecs[0].iov_base[0..iovecs[0].iov_len];
+ try self.write(first_buffer);
+ return first_buffer.len;
+ } else {
+ return os.writev(self.handle, iovecs);
+ }
+ }
+
+ /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
+ /// order to handle partial writes from the underlying OS layer.
+ /// See https://github.com/ziglang/zig/issues/7699
+ /// See equivalent function: `std.fs.File.writevAll`.
+ pub fn writevAll(self: Stream, iovecs: []os.iovec_const) WriteError!void {
+ if (iovecs.len == 0) return;
+
+ var i: usize = 0;
+ while (true) {
+ var amt = try self.writev(iovecs[i..]);
+ while (amt >= iovecs[i].iov_len) {
+ amt -= iovecs[i].iov_len;
+ i += 1;
+ if (i >= iovecs.len) return;
+ }
+ iovecs[i].iov_base += amt;
+ iovecs[i].iov_len -= amt;
+ }
+ }
};
pub const StreamServer = struct {
From 153cd4da0c723fe949bbf9eaf9d780500320bffc Mon Sep 17 00:00:00 2001
From: Michael Dusan
Date: Fri, 19 Feb 2021 15:52:34 -0500
Subject: [PATCH 05/20] macos: fix cond to enable ZIG_SYSTEM_LINKER_HACK
closes #8037
---
src/Compilation.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Compilation.zig b/src/Compilation.zig
index c7bb260aa7..ae3385b2dc 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -645,7 +645,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
};
const darwin_options: DarwinOptions = if (build_options.have_llvm and comptime std.Target.current.isDarwin()) outer: {
- const opts: DarwinOptions = if (use_lld and options.is_native_os and options.target.isDarwin()) inner: {
+ const opts: DarwinOptions = if (use_lld and std.builtin.os.tag == .macos and options.target.isDarwin()) inner: {
// TODO Revisit this targeting versions lower than macOS 11 when LLVM 12 is out.
// See https://github.com/ziglang/zig/issues/6996
const at_least_big_sur = options.target.os.getVersionRange().semver.min.major >= 11;
From a5dcd07382aca0d429d31342de28f786c5f7fd20 Mon Sep 17 00:00:00 2001
From: rgreenblatt
Date: Sat, 20 Feb 2021 18:02:10 -0500
Subject: [PATCH 06/20] fix unspecified fmt
---
lib/std/build.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/std/build.zig b/lib/std/build.zig
index 77ca854f15..65a53ef868 100644
--- a/lib/std/build.zig
+++ b/lib/std/build.zig
@@ -543,7 +543,7 @@ pub const Builder = struct {
.Scalar => |s| {
const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
error.Overflow => {
- warn("-D{s} value {} cannot fit into type {s}.\n\n", .{ name, s, @typeName(T) });
+ warn("-D{s} value {s} cannot fit into type {s}.\n\n", .{ name, s, @typeName(T) });
self.markInvalidUserInput();
return null;
},
From 340825a7afeb1909ab81c1e0258b81ff67323a2b Mon Sep 17 00:00:00 2001
From: data-man
Date: Sat, 20 Feb 2021 21:10:08 +0500
Subject: [PATCH 07/20] Add epoll_pwait2 Linux syscall
---
lib/std/os/bits/linux/arm-eabi.zig | 1 +
lib/std/os/bits/linux/arm64.zig | 1 +
lib/std/os/bits/linux/i386.zig | 1 +
lib/std/os/bits/linux/mips.zig | 1 +
lib/std/os/bits/linux/powerpc64.zig | 1 +
lib/std/os/bits/linux/riscv64.zig | 1 +
lib/std/os/bits/linux/sparc64.zig | 1 +
lib/std/os/bits/linux/x86_64.zig | 1 +
8 files changed, 8 insertions(+)
diff --git a/lib/std/os/bits/linux/arm-eabi.zig b/lib/std/os/bits/linux/arm-eabi.zig
index 4d5accd133..5673cb952b 100644
--- a/lib/std/os/bits/linux/arm-eabi.zig
+++ b/lib/std/os/bits/linux/arm-eabi.zig
@@ -412,6 +412,7 @@ pub const SYS = extern enum(usize) {
pidfd_getfd = 438,
faccessat2 = 439,
process_madvise = 440,
+ epoll_pwait2 = 441,
breakpoint = 0x0f0001,
cacheflush = 0x0f0002,
diff --git a/lib/std/os/bits/linux/arm64.zig b/lib/std/os/bits/linux/arm64.zig
index 7d08010fe8..a069b6adf1 100644
--- a/lib/std/os/bits/linux/arm64.zig
+++ b/lib/std/os/bits/linux/arm64.zig
@@ -313,6 +313,7 @@ pub const SYS = extern enum(usize) {
pidfd_getfd = 438,
faccessat2 = 439,
process_madvise = 440,
+ epoll_pwait2 = 441,
_,
};
diff --git a/lib/std/os/bits/linux/i386.zig b/lib/std/os/bits/linux/i386.zig
index 0e2f1c01aa..7ef34eb96b 100644
--- a/lib/std/os/bits/linux/i386.zig
+++ b/lib/std/os/bits/linux/i386.zig
@@ -448,6 +448,7 @@ pub const SYS = extern enum(usize) {
pidfd_getfd = 438,
faccessat2 = 439,
process_madvise = 440,
+ epoll_pwait2 = 441,
_,
};
diff --git a/lib/std/os/bits/linux/mips.zig b/lib/std/os/bits/linux/mips.zig
index c9621735ee..412a1f48be 100644
--- a/lib/std/os/bits/linux/mips.zig
+++ b/lib/std/os/bits/linux/mips.zig
@@ -430,6 +430,7 @@ pub const SYS = extern enum(usize) {
pidfd_getfd = Linux + 438,
faccessat2 = Linux + 439,
process_madvise = Linux + 440,
+ epoll_pwait2 = Linux + 441,
_,
};
diff --git a/lib/std/os/bits/linux/powerpc64.zig b/lib/std/os/bits/linux/powerpc64.zig
index cc100d7ec1..e0e9347aa1 100644
--- a/lib/std/os/bits/linux/powerpc64.zig
+++ b/lib/std/os/bits/linux/powerpc64.zig
@@ -409,6 +409,7 @@ pub const SYS = extern enum(usize) {
pidfd_getfd = 438,
faccessat2 = 439,
process_madvise = 440,
+ epoll_pwait2 = 441,
_,
};
diff --git a/lib/std/os/bits/linux/riscv64.zig b/lib/std/os/bits/linux/riscv64.zig
index 804d8dbf5e..0cbdea415c 100644
--- a/lib/std/os/bits/linux/riscv64.zig
+++ b/lib/std/os/bits/linux/riscv64.zig
@@ -310,6 +310,7 @@ pub const SYS = extern enum(usize) {
pidfd_getfd = 438,
faccessat2 = 439,
process_madvise = 440,
+ epoll_pwait2 = 441,
_,
};
diff --git a/lib/std/os/bits/linux/sparc64.zig b/lib/std/os/bits/linux/sparc64.zig
index e2e34c39e4..5644256a95 100644
--- a/lib/std/os/bits/linux/sparc64.zig
+++ b/lib/std/os/bits/linux/sparc64.zig
@@ -387,6 +387,7 @@ pub const SYS = extern enum(usize) {
pidfd_getfd = 438,
faccessat2 = 439,
process_madvise = 440,
+ epoll_pwait2 = 441,
_,
};
diff --git a/lib/std/os/bits/linux/x86_64.zig b/lib/std/os/bits/linux/x86_64.zig
index b16190c952..52fee679c5 100644
--- a/lib/std/os/bits/linux/x86_64.zig
+++ b/lib/std/os/bits/linux/x86_64.zig
@@ -375,6 +375,7 @@ pub const SYS = extern enum(usize) {
pidfd_getfd = 438,
faccessat2 = 439,
process_madvise = 440,
+ epoll_pwait2 = 441,
_,
};
From f9be7471bc9a35fcb872a83564d41cdf561ca173 Mon Sep 17 00:00:00 2001
From: rgreenblatt
Date: Sat, 13 Feb 2021 20:17:09 -0500
Subject: [PATCH 08/20] fix readable slice bug (and add tests)
---
lib/std/fifo.zig | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig
index dfd932cb32..b21cb7fc1c 100644
--- a/lib/std/fifo.zig
+++ b/lib/std/fifo.zig
@@ -153,7 +153,7 @@ pub fn LinearFifo(
var start = self.head + offset;
if (start >= self.buf.len) {
start -= self.buf.len;
- return self.buf[start .. self.count - offset];
+ return self.buf[start .. start + (self.count - offset)];
} else {
const end = math.min(self.head + self.count, self.buf.len);
return self.buf[start..end];
@@ -427,6 +427,8 @@ test "LinearFifo(u8, .Dynamic)" {
fifo.writeAssumeCapacity("6
Date: Wed, 17 Feb 2021 20:28:12 -0700
Subject: [PATCH 09/20] remove z/Z format specifier deprecations
The z/Z format specifiers were merged last October (4 months ago). They were then deprecated in January (just over a month ago). This PR removes them altogether.
---
lib/std/fmt.zig | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig
index c1b24cc6da..86d89a7578 100644
--- a/lib/std/fmt.zig
+++ b/lib/std/fmt.zig
@@ -524,7 +524,7 @@ pub fn formatType(
if (actual_fmt.len == 0)
@compileError("cannot format array ref without a specifier (i.e. {s} or {*})");
if (info.child == u8) {
- if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) {
+ if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) {
return formatText(value, actual_fmt, options, writer);
}
}
@@ -542,7 +542,7 @@ pub fn formatType(
return formatType(mem.span(value), actual_fmt, options, writer, max_depth);
}
if (ptr_info.child == u8) {
- if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) {
+ if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) {
return formatText(mem.span(value), actual_fmt, options, writer);
}
}
@@ -555,7 +555,7 @@ pub fn formatType(
return writer.writeAll("{ ... }");
}
if (ptr_info.child == u8) {
- if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) {
+ if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) {
return formatText(value, actual_fmt, options, writer);
}
}
@@ -576,7 +576,7 @@ pub fn formatType(
return writer.writeAll("{ ... }");
}
if (info.child == u8) {
- if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) {
+ if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) {
return formatText(&value, actual_fmt, options, writer);
}
}
@@ -658,8 +658,6 @@ pub fn formatIntValue(
} else {
@compileError("Cannot print integer that is larger than 8 bits as a ascii");
}
- } else if (comptime std.mem.eql(u8, fmt, "Z")) {
- @compileError("specifier 'Z' has been deprecated, wrap your argument in std.zig.fmtEscapes instead");
} else if (comptime std.mem.eql(u8, fmt, "u")) {
if (@typeInfo(@TypeOf(int_value)).Int.bits <= 21) {
return formatUnicodeCodepoint(@as(u21, int_value), options, writer);
@@ -735,10 +733,6 @@ pub fn formatText(
}
}
return;
- } else if (comptime std.mem.eql(u8, fmt, "z")) {
- @compileError("specifier 'z' has been deprecated, wrap your argument in std.zig.fmtId instead");
- } else if (comptime std.mem.eql(u8, fmt, "Z")) {
- @compileError("specifier 'Z' has been deprecated, wrap your argument in std.zig.fmtEscapes instead");
} else {
@compileError("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");
}
From c70832bc411388c3e9749013d7cf99704ff95a36 Mon Sep 17 00:00:00 2001
From: Benjamin Graf
Date: Sat, 13 Feb 2021 20:24:52 +0100
Subject: [PATCH 10/20] replace ArrayList.shrinkAndFree by
ArrayList.shrinkRetainingCapacity
---
lib/std/fs.zig | 2 +-
lib/std/io/reader.zig | 2 +-
lib/std/json.zig | 2 +-
lib/std/math/big/int.zig | 2 +-
lib/std/net.zig | 4 ++--
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index 17c0cb7b1d..53108ebe23 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -2186,7 +2186,7 @@ pub const Walker = struct {
var top = &self.stack.items[self.stack.items.len - 1];
const dirname_len = top.dirname_len;
if (try top.dir_it.next()) |base| {
- self.name_buffer.shrinkAndFree(dirname_len);
+ self.name_buffer.shrinkRetainingCapacity(dirname_len);
try self.name_buffer.append(path.sep);
try self.name_buffer.appendSlice(base.name);
if (base.kind == .Directory) {
diff --git a/lib/std/io/reader.zig b/lib/std/io/reader.zig
index e174051518..916e2155fa 100644
--- a/lib/std/io/reader.zig
+++ b/lib/std/io/reader.zig
@@ -111,7 +111,7 @@ pub fn Reader(
delimiter: u8,
max_size: usize,
) !void {
- array_list.shrinkAndFree(0);
+ array_list.shrinkRetainingCapacity(0);
while (true) {
var byte: u8 = try self.readByte();
diff --git a/lib/std/json.zig b/lib/std/json.zig
index ff258c7f3a..f9fc371049 100644
--- a/lib/std/json.zig
+++ b/lib/std/json.zig
@@ -2018,7 +2018,7 @@ pub const Parser = struct {
pub fn reset(p: *Parser) void {
p.state = .Simple;
- p.stack.shrinkAndFree(0);
+ p.stack.shrinkRetainingCapacity(0);
}
pub fn parse(p: *Parser, input: []const u8) !ValueTree {
diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig
index 81982eac51..d1d7b33508 100644
--- a/lib/std/math/big/int.zig
+++ b/lib/std/math/big/int.zig
@@ -607,7 +607,7 @@ pub const Mutable = struct {
/// it will have the same length as it had when the function was called.
pub fn gcd(rma: *Mutable, x: Const, y: Const, limbs_buffer: *std.ArrayList(Limb)) !void {
const prev_len = limbs_buffer.items.len;
- defer limbs_buffer.shrinkAndFree(prev_len);
+ defer limbs_buffer.shrinkRetainingCapacity(prev_len);
const x_copy = if (rma.limbs.ptr == x.limbs.ptr) blk: {
const start = limbs_buffer.items.len;
try limbs_buffer.appendSlice(x.limbs);
diff --git a/lib/std/net.zig b/lib/std/net.zig
index 2d62a27cd9..636596c117 100644
--- a/lib/std/net.zig
+++ b/lib/std/net.zig
@@ -1205,13 +1205,13 @@ fn linuxLookupNameFromDnsSearch(
var tok_it = mem.tokenize(search, " \t");
while (tok_it.next()) |tok| {
- canon.shrinkAndFree(canon_name.len + 1);
+ canon.shrinkRetainingCapacity(canon_name.len + 1);
try canon.appendSlice(tok);
try linuxLookupNameFromDns(addrs, canon, canon.items, family, rc, port);
if (addrs.items.len != 0) return;
}
- canon.shrinkAndFree(canon_name.len);
+ canon.shrinkRetainingCapacity(canon_name.len);
return linuxLookupNameFromDns(addrs, canon, name, family, rc, port);
}
From 840331ee48e54b1ce4a3c8196f90eec73ce6deea Mon Sep 17 00:00:00 2001
From: Tau
Date: Mon, 25 Jan 2021 07:23:03 +0100
Subject: [PATCH 11/20] Rebase link(at) properly
---
lib/std/c.zig | 2 ++
lib/std/os.zig | 86 ++++++++++++++++++++++++++++++++++++++++++++
lib/std/os/linux.zig | 31 ++++++++++++++++
lib/std/os/test.zig | 69 +++++++++++++++++++++++++++++++++++
4 files changed, 188 insertions(+)
diff --git a/lib/std/c.zig b/lib/std/c.zig
index b5ee8cd893..1688824dd9 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -100,6 +100,8 @@ pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: u64)
pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: u64) *c_void;
pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int;
pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int;
+pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int;
+pub extern "c" fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: c_int) c_int;
pub extern "c" fn unlink(path: [*:0]const u8) c_int;
pub extern "c" fn unlinkat(dirfd: fd_t, path: [*:0]const u8, flags: c_uint) c_int;
pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;
diff --git a/lib/std/os.zig b/lib/std/os.zig
index 02eec2de71..c2431ff12c 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -1634,6 +1634,92 @@ pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:
}
}
+pub const LinkError = UnexpectedError || error{
+ AccessDenied,
+ DiskQuota,
+ PathAlreadyExists,
+ FileSystem,
+ SymLinkLoop,
+ LinkQuotaExceeded,
+ NameTooLong,
+ FileNotFound,
+ SystemResources,
+ NoSpaceLeft,
+ ReadOnlyFileSystem,
+ NotSameFileSystem,
+};
+
+pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkError!void {
+ switch (errno(system.link(oldpath, newpath, flags))) {
+ 0 => return,
+ EACCES => return error.AccessDenied,
+ EDQUOT => return error.DiskQuota,
+ EEXIST => return error.PathAlreadyExists,
+ EFAULT => unreachable,
+ EIO => return error.FileSystem,
+ ELOOP => return error.SymLinkLoop,
+ EMLINK => return error.LinkQuotaExceeded,
+ ENAMETOOLONG => return error.NameTooLong,
+ ENOENT => return error.FileNotFound,
+ ENOMEM => return error.SystemResources,
+ ENOSPC => return error.NoSpaceLeft,
+ EPERM => return error.AccessDenied,
+ EROFS => return error.ReadOnlyFileSystem,
+ EXDEV => return error.NotSameFileSystem,
+ EINVAL => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+}
+
+pub fn link(oldpath: []const u8, newpath: []const u8, flags: i32) LinkError!void {
+ const old = try toPosixPath(oldpath);
+ const new = try toPosixPath(newpath);
+ return try linkZ(&old, &new, flags);
+}
+
+pub const LinkatError = LinkError || error{NotDir};
+
+pub fn linkatZ(
+ olddir: fd_t,
+ oldpath: [*:0]const u8,
+ newdir: fd_t,
+ newpath: [*:0]const u8,
+ flags: i32,
+) LinkatError!void {
+ switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) {
+ 0 => return,
+ EACCES => return error.AccessDenied,
+ EDQUOT => return error.DiskQuota,
+ EEXIST => return error.PathAlreadyExists,
+ EFAULT => unreachable,
+ EIO => return error.FileSystem,
+ ELOOP => return error.SymLinkLoop,
+ EMLINK => return error.LinkQuotaExceeded,
+ ENAMETOOLONG => return error.NameTooLong,
+ ENOENT => return error.FileNotFound,
+ ENOMEM => return error.SystemResources,
+ ENOSPC => return error.NoSpaceLeft,
+ ENOTDIR => return error.NotDir,
+ EPERM => return error.AccessDenied,
+ EROFS => return error.ReadOnlyFileSystem,
+ EXDEV => return error.NotSameFileSystem,
+ EINVAL => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+}
+
+pub fn linkat(
+ olddir: fd_t,
+ oldpath: []const u8,
+ newdir: fd_t,
+ newpath: []const u8,
+ flags: i32,
+) LinkatError!void {
+ const old = try toPosixPath(oldpath);
+ const new = try toPosixPath(newpath);
+ return try linkatZ(olddir, &old, newdir, &new, flags);
+}
+
pub const UnlinkError = error{
FileNotFound,
diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig
index b7534db191..035cdabe63 100644
--- a/lib/std/os/linux.zig
+++ b/lib/std/os/linux.zig
@@ -634,6 +634,37 @@ pub fn tgkill(tgid: pid_t, tid: pid_t, sig: i32) usize {
return syscall2(.tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig)));
}
+pub fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) usize {
+ if (@hasField(SYS, "link")) {
+ return syscall3(
+ .link,
+ @ptrToInt(oldpath),
+ @ptrToInt(newpath),
+ @bitCast(usize, @as(isize, flags)),
+ );
+ } else {
+ return syscall5(
+ .linkat,
+ @bitCast(usize, @as(isize, AT_FDCWD)),
+ @ptrToInt(oldpath),
+ @bitCast(usize, @as(isize, AT_FDCWD)),
+ @ptrToInt(newpath),
+ @bitCast(usize, @as(isize, flags)),
+ );
+ }
+}
+
+pub fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: i32) usize {
+ return syscall5(
+ .linkat,
+ @bitCast(usize, @as(isize, oldfd)),
+ @ptrToInt(oldpath),
+ @bitCast(usize, @as(isize, newfd)),
+ @ptrToInt(newpath),
+ @bitCast(usize, @as(isize, flags)),
+ );
+}
+
pub fn unlink(path: [*:0]const u8) usize {
if (@hasField(SYS, "unlink")) {
return syscall1(.unlink, @ptrToInt(path));
diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig
index 0904a8585f..f08d4d58fa 100644
--- a/lib/std/os/test.zig
+++ b/lib/std/os/test.zig
@@ -189,6 +189,75 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {
expect(mem.eql(u8, target_path, given));
}
+test "link with relative paths" {
+ if (builtin.os.tag != .linux) return error.SkipZigTest;
+ var cwd = fs.cwd();
+
+ cwd.deleteFile("example.txt") catch {};
+ cwd.deleteFile("new.txt") catch {};
+
+ try cwd.writeFile("example.txt", "example");
+ try os.link("example.txt", "new.txt", 0);
+
+ const efd = try cwd.openFile("example.txt", .{});
+ defer efd.close();
+
+ const nfd = try cwd.openFile("new.txt", .{});
+ defer nfd.close();
+
+ {
+ const estat = try os.fstat(efd.handle);
+ const nstat = try os.fstat(nfd.handle);
+
+ testing.expectEqual(estat.ino, nstat.ino);
+ testing.expectEqual(@as(usize, 2), nstat.nlink);
+ }
+
+ try os.unlink("new.txt");
+
+ {
+ const estat = try os.fstat(efd.handle);
+ testing.expectEqual(@as(usize, 1), estat.nlink);
+ }
+
+ try cwd.deleteFile("example.txt");
+}
+
+test "linkat with different directories" {
+ if (builtin.os.tag != .linux) return error.SkipZigTest;
+ var cwd = fs.cwd();
+ var tmp = tmpDir(.{});
+
+ cwd.deleteFile("example.txt") catch {};
+ tmp.dir.deleteFile("new.txt") catch {};
+
+ try cwd.writeFile("example.txt", "example");
+ try os.linkat(cwd.fd, "example.txt", tmp.dir.fd, "new.txt", 0);
+
+ const efd = try cwd.openFile("example.txt", .{});
+ defer efd.close();
+
+ const nfd = try tmp.dir.openFile("new.txt", .{});
+
+ {
+ defer nfd.close();
+ const estat = try os.fstat(efd.handle);
+ const nstat = try os.fstat(nfd.handle);
+
+ testing.expectEqual(estat.ino, nstat.ino);
+ testing.expectEqual(@as(usize, 2), nstat.nlink);
+ }
+
+ try os.unlinkat(tmp.dir.fd, "new.txt", 0);
+
+ {
+ const estat = try os.fstat(efd.handle);
+ testing.expectEqual(@as(usize, 1), estat.nlink);
+ }
+
+ try cwd.deleteFile("example.txt");
+}
+
test "fstatat" {
// enable when `fstat` and `fstatat` are implemented on Windows
if (builtin.os.tag == .windows) return error.SkipZigTest;
From 1bd434fd18e0cb769ca46849dac056a562ce7ce3 Mon Sep 17 00:00:00 2001
From: jacob gw
Date: Mon, 1 Feb 2021 09:18:52 -0500
Subject: [PATCH 12/20] std.Progress: improve support for "dumb" terminals
---
lib/std/Progress.zig | 18 +++++++++++++++++-
src/Compilation.zig | 4 +++-
src/stage1.zig | 4 +++-
3 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig
index 131aff2d2b..4afd191b93 100644
--- a/lib/std/Progress.zig
+++ b/lib/std/Progress.zig
@@ -25,6 +25,13 @@ terminal: ?std.fs.File = undefined,
/// Whether the terminal supports ANSI escape codes.
supports_ansi_escape_codes: bool = false,
+/// If the terminal is "dumb", don't print output.
+/// This can be useful if you don't want to print all
+/// the stages of code generation if there are a lot.
+/// You should not use it if the user should see output
+/// for example showing the user what tests run.
+dont_print_on_dumb: bool = false,
+
root: Node = undefined,
/// Keeps track of how much time has passed since the beginning.
@@ -141,6 +148,9 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*
self.supports_ansi_escape_codes = true;
} else if (std.builtin.os.tag == .windows and stderr.isTty()) {
self.terminal = stderr;
+ } else if (std.builtin.os.tag != .windows) {
+ // we are in a "dumb" terminal like in acme or writing to a file
+ self.terminal = stderr;
}
self.root = Node{
.context = self,
@@ -178,6 +188,8 @@ pub fn refresh(self: *Progress) void {
}
fn refreshWithHeldLock(self: *Progress) void {
+ const is_dumb = !self.supports_ansi_escape_codes and !(std.builtin.os.tag == .windows);
+ if (is_dumb and self.dont_print_on_dumb) return;
const file = self.terminal orelse return;
const prev_columns_written = self.columns_written;
@@ -226,7 +238,11 @@ fn refreshWithHeldLock(self: *Progress) void {
if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE)
unreachable;
- } else unreachable;
+ } else {
+ // we are in a "dumb" terminal like in acme or writing to a file
+ self.output_buffer[end] = '\n';
+ end += 1;
+ }
self.columns_written = 0;
}
diff --git a/src/Compilation.zig b/src/Compilation.zig
index ae3385b2dc..180d49a196 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -1538,7 +1538,9 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 {
}
pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void {
- var progress: std.Progress = .{};
+ // If the terminal is dumb, we dont want to show the user all the
+ // output.
+ var progress: std.Progress = .{ .dont_print_on_dumb = true };
var main_progress_node = try progress.start("", 0);
defer main_progress_node.end();
if (self.color == .off) progress.terminal = null;
diff --git a/src/stage1.zig b/src/stage1.zig
index 8ab3b1d94d..1b7eadd1a8 100644
--- a/src/stage1.zig
+++ b/src/stage1.zig
@@ -278,7 +278,9 @@ export fn stage2_attach_segfault_handler() void {
// ABI warning
export fn stage2_progress_create() *std.Progress {
const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory");
- ptr.* = std.Progress{};
+ // If the terminal is dumb, we dont want to show the user all the
+ // output.
+ ptr.* = std.Progress{ .dont_print_on_dumb = true };
return ptr;
}
From cc5e5cca83cca9a1de81f98a333e8a3fcd26df0c Mon Sep 17 00:00:00 2001
From: Bill Nagel
Date: Mon, 11 Jan 2021 22:21:18 -0500
Subject: [PATCH 13/20] fix race condition in linuxWaitFd
---
lib/std/event/loop.zig | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig
index 492b7c1758..912f99e961 100644
--- a/lib/std/event/loop.zig
+++ b/lib/std/event/loop.zig
@@ -440,13 +440,11 @@ pub const Loop = struct {
.overlapped = ResumeNode.overlapped_init,
},
};
- var need_to_delete = false;
+ var need_to_delete = true;
defer if (need_to_delete) self.linuxRemoveFd(fd);
suspend {
- if (self.linuxAddFd(fd, &resume_node.base, flags)) |_| {
- need_to_delete = true;
- } else |err| switch (err) {
+ self.linuxAddFd(fd, &resume_node.base, flags) catch |err| switch (err) {
error.FileDescriptorNotRegistered => unreachable,
error.OperationCausesCircularLoop => unreachable,
error.FileDescriptorIncompatibleWithEpoll => unreachable,
@@ -456,6 +454,7 @@ pub const Loop = struct {
error.UserResourceLimitReached,
error.Unexpected,
=> {
+ need_to_delete = false;
// Fall back to a blocking poll(). Ideally this codepath is never hit, since
// epoll should be just fine. But this is better than incorrect behavior.
var poll_flags: i16 = 0;
@@ -479,7 +478,7 @@ pub const Loop = struct {
};
resume @frame();
},
- }
+ };
}
}
From 4272f07f668979bc356f117cdf12986a95402b43 Mon Sep 17 00:00:00 2001
From: Asherah Connor <1915+kivikakk@users.noreply.github.com>
Date: Sun, 21 Feb 2021 21:17:59 +1100
Subject: [PATCH 14/20] std.os.uefi.Guid fixes (#8032)
* uefi: Guid.format compiles again
Also use "writer" nomenclature in argument name.
* uefi: add Guid.eql
---
lib/std/os/uefi.zig | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig
index 6cfa2d24e4..1942165999 100644
--- a/lib/std/os/uefi.zig
+++ b/lib/std/os/uefi.zig
@@ -3,6 +3,8 @@
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
+const std = @import("../std.zig");
+
/// A protocol is an interface identified by a GUID.
pub const protocols = @import("uefi/protocols.zig");
@@ -33,10 +35,10 @@ pub const Guid = extern struct {
self: @This(),
comptime f: []const u8,
options: std.fmt.FormatOptions,
- out_stream: anytype,
- ) Errors!void {
+ writer: anytype,
+ ) !void {
if (f.len == 0) {
- return std.fmt.format(out_stream, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{
+ return std.fmt.format(writer, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{
self.time_low,
self.time_mid,
self.time_high_and_version,
@@ -48,6 +50,15 @@ pub const Guid = extern struct {
@compileError("Unknown format character: '" ++ f ++ "'");
}
}
+
+ pub fn eql(a: std.os.uefi.Guid, b: std.os.uefi.Guid) bool {
+ return a.time_low == b.time_low and
+ a.time_mid == b.time_mid and
+ a.time_high_and_version == b.time_high_and_version and
+ a.clock_seq_high_and_reserved == b.clock_seq_high_and_reserved and
+ a.clock_seq_low == b.clock_seq_low and
+ std.mem.eql(u8, &a.node, &b.node);
+ }
};
/// An EFI Handle represents a collection of related interfaces.
From 057bf1afc9933e32bd35842d2464dab2164f06fb Mon Sep 17 00:00:00 2001
From: LemonBoy
Date: Thu, 18 Feb 2021 20:28:59 +0100
Subject: [PATCH 15/20] std: Add more error checking in hexToBytes
Prevent the function from turning into an endless loop that may or may
not perform OOB accesses.
---
lib/std/crypto/25519/ed25519.zig | 10 +++++-----
lib/std/crypto/25519/ristretto255.zig | 2 +-
lib/std/crypto/25519/x25519.zig | 4 ++--
lib/std/crypto/aes.zig | 8 ++++----
lib/std/crypto/blake3.zig | 2 +-
lib/std/crypto/gimli.zig | 22 +++++++++++-----------
lib/std/fmt.zig | 25 ++++++++++++++++++-------
src/Cache.zig | 2 +-
8 files changed, 43 insertions(+), 32 deletions(-)
diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig
index 420eb33a30..5c7ec0cdac 100644
--- a/lib/std/crypto/25519/ed25519.zig
+++ b/lib/std/crypto/25519/ed25519.zig
@@ -207,7 +207,7 @@ pub const Ed25519 = struct {
test "ed25519 key pair creation" {
var seed: [32]u8 = undefined;
- try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
+ _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
const key_pair = try Ed25519.KeyPair.create(seed);
var buf: [256]u8 = undefined;
std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair.secret_key}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
@@ -216,7 +216,7 @@ test "ed25519 key pair creation" {
test "ed25519 signature" {
var seed: [32]u8 = undefined;
- try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
+ _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
const key_pair = try Ed25519.KeyPair.create(seed);
const sig = try Ed25519.sign("test", key_pair, null);
@@ -339,11 +339,11 @@ test "ed25519 test vectors" {
};
for (entries) |entry, i| {
var msg: [entry.msg_hex.len / 2]u8 = undefined;
- try fmt.hexToBytes(&msg, entry.msg_hex);
+ _ = try fmt.hexToBytes(&msg, entry.msg_hex);
var public_key: [32]u8 = undefined;
- try fmt.hexToBytes(&public_key, entry.public_key_hex);
+ _ = try fmt.hexToBytes(&public_key, entry.public_key_hex);
var sig: [64]u8 = undefined;
- try fmt.hexToBytes(&sig, entry.sig_hex);
+ _ = try fmt.hexToBytes(&sig, entry.sig_hex);
if (entry.expected) |error_type| {
std.testing.expectError(error_type, Ed25519.verify(sig, &msg, public_key));
} else {
diff --git a/lib/std/crypto/25519/ristretto255.zig b/lib/std/crypto/25519/ristretto255.zig
index 68fb938103..df85422f65 100644
--- a/lib/std/crypto/25519/ristretto255.zig
+++ b/lib/std/crypto/25519/ristretto255.zig
@@ -173,7 +173,7 @@ test "ristretto255" {
std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76");
var r: [Ristretto255.encoded_length]u8 = undefined;
- try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919");
+ _ = try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919");
var q = try Ristretto255.fromBytes(r);
q = q.dbl().add(p);
std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{q.toBytes()}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E");
diff --git a/lib/std/crypto/25519/x25519.zig b/lib/std/crypto/25519/x25519.zig
index 530637a451..5d0479bd4d 100644
--- a/lib/std/crypto/25519/x25519.zig
+++ b/lib/std/crypto/25519/x25519.zig
@@ -85,8 +85,8 @@ const htest = @import("../test.zig");
test "x25519 public key calculation from secret key" {
var sk: [32]u8 = undefined;
var pk_expected: [32]u8 = undefined;
- try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
- try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50");
+ _ = try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
+ _ = try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50");
const pk_calculated = try X25519.recoverPublicKey(sk);
std.testing.expectEqual(pk_calculated, pk_expected);
}
diff --git a/lib/std/crypto/aes.zig b/lib/std/crypto/aes.zig
index d862bcf3fc..2a81492c8a 100644
--- a/lib/std/crypto/aes.zig
+++ b/lib/std/crypto/aes.zig
@@ -122,11 +122,11 @@ test "expand 128-bit key" {
var exp: [16]u8 = undefined;
for (enc.key_schedule.round_keys) |round_key, i| {
- try std.fmt.hexToBytes(&exp, exp_enc[i]);
+ _ = try std.fmt.hexToBytes(&exp, exp_enc[i]);
testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
}
for (enc.key_schedule.round_keys) |round_key, i| {
- try std.fmt.hexToBytes(&exp, exp_dec[i]);
+ _ = try std.fmt.hexToBytes(&exp, exp_dec[i]);
testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
}
}
@@ -144,11 +144,11 @@ test "expand 256-bit key" {
var exp: [16]u8 = undefined;
for (enc.key_schedule.round_keys) |round_key, i| {
- try std.fmt.hexToBytes(&exp, exp_enc[i]);
+ _ = try std.fmt.hexToBytes(&exp, exp_enc[i]);
testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
}
for (dec.key_schedule.round_keys) |round_key, i| {
- try std.fmt.hexToBytes(&exp, exp_dec[i]);
+ _ = try std.fmt.hexToBytes(&exp, exp_dec[i]);
testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
}
}
diff --git a/lib/std/crypto/blake3.zig b/lib/std/crypto/blake3.zig
index 7a65487135..a10c50b074 100644
--- a/lib/std/crypto/blake3.zig
+++ b/lib/std/crypto/blake3.zig
@@ -663,7 +663,7 @@ fn testBlake3(hasher: *Blake3, input_len: usize, expected_hex: [262]u8) void {
// Compare to expected value
var expected_bytes: [expected_hex.len / 2]u8 = undefined;
- fmt.hexToBytes(expected_bytes[0..], expected_hex[0..]) catch unreachable;
+ _ = fmt.hexToBytes(expected_bytes[0..], expected_hex[0..]) catch unreachable;
testing.expectEqual(actual_bytes, expected_bytes);
// Restore initial state
diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig
index 4809b9b6f7..1c1d6c79db 100644
--- a/lib/std/crypto/gimli.zig
+++ b/lib/std/crypto/gimli.zig
@@ -270,7 +270,7 @@ pub fn hash(out: []u8, in: []const u8, options: Hash.Options) void {
test "hash" {
// a test vector (30) from NIST KAT submission.
var msg: [58 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C");
+ _ = try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C");
var md: [32]u8 = undefined;
hash(&md, &msg, .{});
htest.assertEqual("1C9A03DC6A5DDC5444CFC6F4B154CFF5CF081633B2CEA4D7D0AE7CCFED5AAA44", &md);
@@ -278,7 +278,7 @@ test "hash" {
test "hash test vector 17" {
var msg: [32 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F");
+ _ = try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F");
var md: [32]u8 = undefined;
hash(&md, &msg, .{});
htest.assertEqual("404C130AF1B9023A7908200919F690FFBB756D5176E056FFDE320016A37C7282", &md);
@@ -286,7 +286,7 @@ test "hash test vector 17" {
test "hash test vector 33" {
var msg: [32]u8 = undefined;
- try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+ _ = try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
var md: [32]u8 = undefined;
hash(&md, &msg, .{});
htest.assertEqual("A8F4FA28708BDA7EFB4C1914CA4AFA9E475B82D588D36504F87DBB0ED9AB3C4B", &md);
@@ -436,9 +436,9 @@ pub const Aead = struct {
test "cipher" {
var key: [32]u8 = undefined;
- try std.fmt.hexToBytes(&key, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+ _ = try std.fmt.hexToBytes(&key, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
var nonce: [16]u8 = undefined;
- try std.fmt.hexToBytes(&nonce, "000102030405060708090A0B0C0D0E0F");
+ _ = try std.fmt.hexToBytes(&nonce, "000102030405060708090A0B0C0D0E0F");
{ // test vector (1) from NIST KAT submission.
const ad: [0]u8 = undefined;
const pt: [0]u8 = undefined;
@@ -456,7 +456,7 @@ test "cipher" {
{ // test vector (34) from NIST KAT submission.
const ad: [0]u8 = undefined;
var pt: [2 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&pt, "00");
+ _ = try std.fmt.hexToBytes(&pt, "00");
var ct: [pt.len]u8 = undefined;
var tag: [16]u8 = undefined;
@@ -470,9 +470,9 @@ test "cipher" {
}
{ // test vector (106) from NIST KAT submission.
var ad: [12 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&ad, "000102030405");
+ _ = try std.fmt.hexToBytes(&ad, "000102030405");
var pt: [6 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&pt, "000102");
+ _ = try std.fmt.hexToBytes(&pt, "000102");
var ct: [pt.len]u8 = undefined;
var tag: [16]u8 = undefined;
@@ -486,9 +486,9 @@ test "cipher" {
}
{ // test vector (790) from NIST KAT submission.
var ad: [60 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&ad, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D");
+ _ = try std.fmt.hexToBytes(&ad, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D");
var pt: [46 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F10111213141516");
+ _ = try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F10111213141516");
var ct: [pt.len]u8 = undefined;
var tag: [16]u8 = undefined;
@@ -503,7 +503,7 @@ test "cipher" {
{ // test vector (1057) from NIST KAT submission.
const ad: [0]u8 = undefined;
var pt: [64 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+ _ = try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
var ct: [pt.len]u8 = undefined;
var tag: [16]u8 = undefined;
diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig
index 86d89a7578..e5ce457091 100644
--- a/lib/std/fmt.zig
+++ b/lib/std/fmt.zig
@@ -1982,23 +1982,34 @@ test "bytes.hex" {
pub const trim = @compileError("deprecated; use std.mem.trim with std.ascii.spaces instead");
pub const isWhiteSpace = @compileError("deprecated; use std.ascii.isSpace instead");
-pub fn hexToBytes(out: []u8, input: []const u8) !void {
- if (out.len * 2 < input.len)
+/// Decodes the sequence of bytes represented by the specified string of
+/// hexadecimal characters.
+/// Returns a slice of the output buffer containing the decoded bytes.
+pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 {
+ // Expect 0 or n pairs of hexadecimal digits.
+ if (input.len & 1 != 0)
return error.InvalidLength;
+ if (out.len * 2 < input.len)
+ return error.NoSpaceLeft;
var in_i: usize = 0;
- while (in_i != input.len) : (in_i += 2) {
+ while (in_i < input.len) : (in_i += 2) {
const hi = try charToDigit(input[in_i], 16);
const lo = try charToDigit(input[in_i + 1], 16);
out[in_i / 2] = (hi << 4) | lo;
}
+
+ return out[0 .. in_i / 2];
}
test "hexToBytes" {
- const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706";
- var pb: [32]u8 = undefined;
- try hexToBytes(pb[0..], test_hex_str);
- try expectFmt(test_hex_str, "{X}", .{pb});
+ var buf: [32]u8 = undefined;
+ try expectFmt("90" ** 32, "{X}", .{try hexToBytes(&buf, "90" ** 32)});
+ try expectFmt("ABCD", "{X}", .{try hexToBytes(&buf, "ABCD")});
+ try expectFmt("", "{X}", .{try hexToBytes(&buf, "")});
+ std.testing.expectError(error.InvalidCharacter, hexToBytes(&buf, "012Z"));
+ std.testing.expectError(error.InvalidLength, hexToBytes(&buf, "AAA"));
+ std.testing.expectError(error.NoSpaceLeft, hexToBytes(buf[0..1], "ABAB"));
}
test "formatIntValue with comptime_int" {
diff --git a/src/Cache.zig b/src/Cache.zig
index f5ffb34dbe..57ff9227fa 100644
--- a/src/Cache.zig
+++ b/src/Cache.zig
@@ -317,7 +317,7 @@ pub const Manifest = struct {
cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
- std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
+ _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
if (file_path.len == 0) {
return error.InvalidFormat;
From 9712e892656ace8ee26c2b2decfde0f2d116ec54 Mon Sep 17 00:00:00 2001
From: joachimschmidt557
Date: Fri, 5 Feb 2021 21:05:14 +0100
Subject: [PATCH 16/20] stage2 codegen: Add Type argument to genSetReg
---
src/codegen.zig | 94 ++++++++++++++++++++++++-------------------------
1 file changed, 46 insertions(+), 48 deletions(-)
diff --git a/src/codegen.zig b/src/codegen.zig
index d81ad1faf5..ea08b80092 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -939,7 +939,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
/// Copies a value to a register without tracking the register. The register is not considered
/// allocated. A second call to `copyToTmpRegister` may return the same register.
/// This can have a side effect of spilling instructions to the stack to free up a register.
- fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register {
+ fn copyToTmpRegister(self: *Self, src: usize, ty: Type, mcv: MCValue) !Register {
const reg = self.findUnusedReg() orelse b: {
// We'll take over the first register. Move the instruction that was previously
// there to a stack allocation.
@@ -956,7 +956,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
break :b reg;
};
- try self.genSetReg(src, reg, mcv);
+ try self.genSetReg(src, ty, reg, mcv);
return reg;
}
@@ -983,7 +983,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
break :b reg;
};
- try self.genSetReg(reg_owner.src, reg, mcv);
+ try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);
return MCValue{ .register = reg };
}
@@ -1351,13 +1351,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
// Load immediate into register if it doesn't fit
// as an operand
break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) orelse
- Instruction.Operand.reg(try self.copyToTmpRegister(src, op2), Instruction.Operand.Shift.none);
+ Instruction.Operand.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), op2), Instruction.Operand.Shift.none);
},
.register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none),
.stack_offset,
.embedded_in_code,
.memory,
- => Instruction.Operand.reg(try self.copyToTmpRegister(src, op2), Instruction.Operand.Shift.none),
+ => Instruction.Operand.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), op2), Instruction.Operand.Shift.none),
};
switch (op) {
@@ -1443,7 +1443,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
switch (src_mcv) {
.immediate => |imm| {
if (imm > math.maxInt(u31)) {
- src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, src_mcv) };
+ src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, Type.initTag(.u64), src_mcv) };
}
},
else => {},
@@ -1474,7 +1474,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.register => |dst_reg| {
switch (src_mcv) {
.none => unreachable,
- .undef => try self.genSetReg(src, dst_reg, .undef),
+ .undef => try self.genSetReg(src, dst_ty, dst_reg, .undef),
.dead, .unreach => unreachable,
.ptr_stack_offset => unreachable,
.ptr_embedded_in_code => unreachable,
@@ -1684,7 +1684,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
switch (mc_arg) {
.none => continue,
.register => |reg| {
- try self.genSetReg(arg.src, reg, arg_mcv);
+ try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
// TODO interact with the register allocator to mark the instruction as moved.
},
.stack_offset => {
@@ -1753,7 +1753,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
else
unreachable;
- try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
+ try self.genSetReg(inst.base.src, Type.initTag(.usize), .ra, .{ .memory = got_addr });
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
} else if (func_value.castTag(.extern_fn)) |_| {
return self.fail(inst.base.src, "TODO implement calling extern functions", .{});
@@ -1826,7 +1826,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.compare_flags_signed => unreachable,
.compare_flags_unsigned => unreachable,
.register => |reg| {
- try self.genSetReg(arg.src, reg, arg_mcv);
+ try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
// TODO interact with the register allocator to mark the instruction as moved.
},
.stack_offset => {
@@ -1854,7 +1854,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
else
unreachable;
- try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });
+ try self.genSetReg(inst.base.src, Type.initTag(.usize), .lr, .{ .memory = got_addr });
// TODO: add Instruction.supportedOn
// function for ARM
@@ -1889,7 +1889,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.compare_flags_signed => unreachable,
.compare_flags_unsigned => unreachable,
.register => |reg| {
- try self.genSetReg(arg.src, reg, arg_mcv);
+ try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
// TODO interact with the register allocator to mark the instruction as moved.
},
.stack_offset => {
@@ -1917,7 +1917,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
else
unreachable;
- try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr });
+ try self.genSetReg(inst.base.src, Type.initTag(.usize), .x30, .{ .memory = got_addr });
writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
} else if (func_value.castTag(.extern_fn)) |_| {
@@ -1940,7 +1940,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
switch (mc_arg) {
.none => continue,
.register => |reg| {
- try self.genSetReg(arg.src, reg, arg_mcv);
+ try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
// TODO interact with the register allocator to mark the instruction as moved.
},
.stack_offset => {
@@ -1973,12 +1973,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
switch (arch) {
.x86_64 => {
- try self.genSetReg(inst.base.src, .rax, .{ .memory = got_addr });
+ try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr });
// callq *%rax
self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
},
.aarch64 => {
- try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr });
+ try self.genSetReg(inst.base.src, Type.initTag(.u32), .x30, .{ .memory = got_addr });
// blr x30
writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
},
@@ -2579,7 +2579,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const reg = parseRegName(reg_name) orelse
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
const arg = try self.resolveInst(inst.args[i]);
- try self.genSetReg(inst.base.src, reg, arg);
+ try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
}
if (mem.eql(u8, inst.asm_source, "svc #0")) {
@@ -2609,7 +2609,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const reg = parseRegName(reg_name) orelse
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
const arg = try self.resolveInst(inst.args[i]);
- try self.genSetReg(inst.base.src, reg, arg);
+ try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
}
if (mem.eql(u8, inst.asm_source, "svc #0")) {
@@ -2641,7 +2641,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const reg = parseRegName(reg_name) orelse
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
const arg = try self.resolveInst(inst.args[i]);
- try self.genSetReg(inst.base.src, reg, arg);
+ try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
}
if (mem.eql(u8, inst.asm_source, "ecall")) {
@@ -2671,7 +2671,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const reg = parseRegName(reg_name) orelse
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
const arg = try self.resolveInst(inst.args[i]);
- try self.genSetReg(inst.base.src, reg, arg);
+ try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
}
if (mem.eql(u8, inst.asm_source, "syscall")) {
@@ -2733,7 +2733,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void {
switch (loc) {
.none => return,
- .register => |reg| return self.genSetReg(src, reg, val),
+ .register => |reg| return self.genSetReg(src, ty, reg, val),
.stack_offset => |off| return self.genSetStack(src, ty, off, val),
.memory => {
return self.fail(src, "TODO implement setRegOrMem for memory", .{});
@@ -2768,7 +2768,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
},
.immediate => {
- const reg = try self.copyToTmpRegister(src, mcv);
+ const reg = try self.copyToTmpRegister(src, ty, mcv);
return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
},
.embedded_in_code => |code_offset| {
@@ -2782,7 +2782,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1, 4 => {
const offset = if (math.cast(u12, adj_off)) |imm| blk: {
break :blk Instruction.Offset.imm(imm);
- } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0);
+ } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
const str = switch (abi_size) {
1 => Instruction.strb,
4 => Instruction.str,
@@ -2797,7 +2797,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2 => {
const offset = if (adj_off <= math.maxInt(u8)) blk: {
break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
- } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }));
+ } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }));
writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{
.offset = offset,
@@ -2814,7 +2814,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
if (stack_offset == off)
return; // Copy stack variable to itself; nothing to do.
- const reg = try self.copyToTmpRegister(src, mcv);
+ const reg = try self.copyToTmpRegister(src, ty, mcv);
return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
},
},
@@ -2903,7 +2903,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
if (stack_offset == off)
return; // Copy stack variable to itself; nothing to do.
- const reg = try self.copyToTmpRegister(src, mcv);
+ const reg = try self.copyToTmpRegister(src, ty, mcv);
return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
},
},
@@ -2931,7 +2931,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
},
.immediate => {
- const reg = try self.copyToTmpRegister(src, mcv);
+ const reg = try self.copyToTmpRegister(src, ty, mcv);
return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
},
.embedded_in_code => |code_offset| {
@@ -2946,7 +2946,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const offset = if (math.cast(i9, adj_off)) |imm|
Instruction.LoadStoreOffset.imm_post_index(-imm)
else |_|
- Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }));
+ Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off }));
const rn: Register = switch (arch) {
.aarch64, .aarch64_be => .x29,
.aarch64_32 => .w29,
@@ -2967,7 +2967,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
if (stack_offset == off)
return; // Copy stack variable to itself; nothing to do.
- const reg = try self.copyToTmpRegister(src, mcv);
+ const reg = try self.copyToTmpRegister(src, ty, mcv);
return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
},
},
@@ -2975,7 +2975,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
}
- fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
+ fn genSetReg(self: *Self, src: usize, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
switch (arch) {
.arm, .armeb => switch (mcv) {
.dead => unreachable,
@@ -2986,7 +2986,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
if (!self.wantSafety())
return; // The already existing value will do just fine.
// Write the debug undefined value.
- return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa });
+ return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa });
},
.compare_flags_unsigned,
.compare_flags_signed,
@@ -3051,21 +3051,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.memory => |addr| {
// The value is in memory at a hard-coded address.
// If the type is a pointer, it means the pointer address is at this memory location.
- try self.genSetReg(src, reg, .{ .immediate = addr });
+ try self.genSetReg(src, ty, reg, .{ .immediate = addr });
writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());
},
.stack_offset => |unadjusted_off| {
// TODO: maybe addressing from sp instead of fp
- // TODO: supply type information to genSetReg as we do to genSetStack
- // const abi_size = ty.abiSize(self.target.*);
- const abi_size = 4;
+ const abi_size = ty.abiSize(self.target.*);
const adj_off = unadjusted_off + abi_size;
switch (abi_size) {
1, 4 => {
const offset = if (adj_off <= math.maxInt(u12)) blk: {
break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
- } else Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0);
+ } else Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
const ldr = switch (abi_size) {
1 => Instruction.ldrb,
4 => Instruction.ldr,
@@ -3080,7 +3078,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2 => {
const offset = if (adj_off <= math.maxInt(u8)) blk: {
break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
- } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }));
+ } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }));
writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{
.offset = offset,
@@ -3102,8 +3100,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return; // The already existing value will do just fine.
// Write the debug undefined value.
switch (reg.size()) {
- 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }),
- 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
+ 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }),
+ 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
else => unreachable, // unexpected register size
}
},
@@ -3216,7 +3214,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
} else {
// The value is in memory at a hard-coded address.
// If the type is a pointer, it means the pointer address is at this memory location.
- try self.genSetReg(src, reg, .{ .immediate = addr });
+ try self.genSetReg(src, Type.initTag(.usize), reg, .{ .immediate = addr });
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32());
}
},
@@ -3231,7 +3229,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
if (!self.wantSafety())
return; // The already existing value will do just fine.
// Write the debug undefined value.
- return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
+ return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
},
.immediate => |unsigned_x| {
const x = @bitCast(i64, unsigned_x);
@@ -3256,7 +3254,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.memory => |addr| {
// The value is in memory at a hard-coded address.
// If the type is a pointer, it means the pointer address is at this memory location.
- try self.genSetReg(src, reg, .{ .immediate = addr });
+ try self.genSetReg(src, ty, reg, .{ .immediate = addr });
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
// LOAD imm=[i12 offset = 0], rs1 =
@@ -3275,10 +3273,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return; // The already existing value will do just fine.
// Write the debug undefined value.
switch (reg.size()) {
- 8 => return self.genSetReg(src, reg, .{ .immediate = 0xaa }),
- 16 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaa }),
- 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }),
- 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
+ 8 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaa }),
+ 16 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaa }),
+ 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }),
+ 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
else => unreachable,
}
},
@@ -3492,7 +3490,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
assert(id3 != 4 and id3 != 5);
// Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
- try self.genSetReg(src, reg, MCValue{ .immediate = x });
+ try self.genSetReg(src, ty, reg, MCValue{ .immediate = x });
// Now, the register contains the address of the value to load into it
// Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
@@ -3591,7 +3589,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
// This immediate is unsigned.
const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed));
if (imm >= math.maxInt(U)) {
- return MCValue{ .register = try self.copyToTmpRegister(inst.src, mcv) };
+ return MCValue{ .register = try self.copyToTmpRegister(inst.src, Type.initTag(.usize), mcv) };
}
},
else => {},
From 36178caf3e34859fc715bb281f2692135a337154 Mon Sep 17 00:00:00 2001
From: Ryan Greenblatt
Date: Sun, 21 Feb 2021 05:26:46 -0500
Subject: [PATCH 17/20] Added support for passing write file args as build
options (#7909)
* Added support for passing write file args as build options
* Fix missing fmtEscapes and unused format
* Actually fixed now, must be formatted
* remove addPathBuildOption
---
lib/std/build.zig | 49 +++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 45 insertions(+), 4 deletions(-)
diff --git a/lib/std/build.zig b/lib/std/build.zig
index 65a53ef868..f52b863d3f 100644
--- a/lib/std/build.zig
+++ b/lib/std/build.zig
@@ -1308,6 +1308,12 @@ const BuildOptionArtifactArg = struct {
artifact: *LibExeObjStep,
};
+const BuildOptionWriteFileArg = struct {
+ name: []const u8,
+ write_file: *WriteFileStep,
+ basename: []const u8,
+};
+
pub const LibExeObjStep = struct {
step: Step,
builder: *Builder,
@@ -1355,6 +1361,7 @@ pub const LibExeObjStep = struct {
packages: ArrayList(Pkg),
build_options_contents: std.ArrayList(u8),
build_options_artifact_args: std.ArrayList(BuildOptionArtifactArg),
+ build_options_write_file_args: std.ArrayList(BuildOptionWriteFileArg),
object_src: []const u8,
@@ -1515,6 +1522,7 @@ pub const LibExeObjStep = struct {
.object_src = undefined,
.build_options_contents = std.ArrayList(u8).init(builder.allocator),
.build_options_artifact_args = std.ArrayList(BuildOptionArtifactArg).init(builder.allocator),
+ .build_options_write_file_args = std.ArrayList(BuildOptionWriteFileArg).init(builder.allocator),
.c_std = Builder.CStd.C99,
.override_lib_dir = null,
.main_pkg_path = null,
@@ -2008,6 +2016,23 @@ pub const LibExeObjStep = struct {
self.step.dependOn(&artifact.step);
}
+ /// The value is the path in the cache dir.
+ /// Adds a dependency automatically.
+ /// basename refers to the basename of the WriteFileStep
+ pub fn addBuildOptionWriteFile(
+ self: *LibExeObjStep,
+ name: []const u8,
+ write_file: *WriteFileStep,
+ basename: []const u8,
+ ) void {
+ self.build_options_write_file_args.append(.{
+ .name = name,
+ .write_file = write_file,
+ .basename = basename,
+ }) catch unreachable;
+ self.step.dependOn(&write_file.step);
+ }
+
pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void {
self.include_dirs.append(IncludeDir{ .RawPathSystem = self.builder.dupe(path) }) catch unreachable;
}
@@ -2228,11 +2253,27 @@ pub const LibExeObjStep = struct {
}
}
- if (self.build_options_contents.items.len > 0 or self.build_options_artifact_args.items.len > 0) {
- // Render build artifact options at the last minute, now that the path is known.
+ if (self.build_options_contents.items.len > 0 or
+ self.build_options_artifact_args.items.len > 0 or
+ self.build_options_write_file_args.items.len > 0)
+ {
+ // Render build artifact and write file options at the last minute, now that the path is known.
+ //
+ // Note that pathFromRoot uses resolve path, so this will have
+ // correct behavior even if getOutputPath is already absolute.
for (self.build_options_artifact_args.items) |item| {
- const out = self.build_options_contents.writer();
- out.print("pub const {s}: []const u8 = \"{}\";\n", .{ item.name, std.zig.fmtEscapes(item.artifact.getOutputPath()) }) catch unreachable;
+ self.addBuildOption(
+ []const u8,
+ item.name,
+ self.builder.pathFromRoot(item.artifact.getOutputPath()),
+ );
+ }
+ for (self.build_options_write_file_args.items) |item| {
+ self.addBuildOption(
+ []const u8,
+ item.name,
+ self.builder.pathFromRoot(item.write_file.getOutputPath(item.basename)),
+ );
}
const build_options_file = try fs.path.join(
From 0aef1faa820e29a9eceb51a2048977d7be938f2a Mon Sep 17 00:00:00 2001
From: ducdetronquito
Date: Sun, 21 Feb 2021 11:55:00 +0100
Subject: [PATCH 18/20] std.fifo.LinearFifo - Expose reader and writer type.
---
lib/std/fifo.zig | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig
index b21cb7fc1c..b0771bba16 100644
--- a/lib/std/fifo.zig
+++ b/lib/std/fifo.zig
@@ -44,6 +44,8 @@ pub fn LinearFifo(
count: usize,
const Self = @This();
+ pub const Reader = std.io.Reader(*Self, error{}, readFn);
+ pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite);
// Type of Self argument for slice operations.
// If buffer is inline (Static) then we need to ensure we haven't
@@ -228,7 +230,7 @@ pub fn LinearFifo(
return self.read(dest);
}
- pub fn reader(self: *Self) std.io.Reader(*Self, error{}, readFn) {
+ pub fn reader(self: *Self) Reader {
return .{ .context = self };
}
@@ -318,7 +320,7 @@ pub fn LinearFifo(
return bytes.len;
}
- pub fn writer(self: *Self) std.io.Writer(*Self, error{OutOfMemory}, appendWrite) {
+ pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
From d9e46dceeca3f66b87e6b2e36415417495d2d2a0 Mon Sep 17 00:00:00 2001
From: johnLate
Date: Mon, 22 Feb 2021 19:29:00 +0100
Subject: [PATCH 19/20] std.Thread.Semaphore: Fix wrong variable name
Fixes ziglang#8052
---
lib/std/Thread/Semaphore.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/std/Thread/Semaphore.zig b/lib/std/Thread/Semaphore.zig
index 77a278b355..a899cd9b6f 100644
--- a/lib/std/Thread/Semaphore.zig
+++ b/lib/std/Thread/Semaphore.zig
@@ -13,7 +13,7 @@ cond: Condition = .{},
//! It is OK to initialize this field to any value.
permits: usize = 0,
-const RwLock = @This();
+const Semaphore = @This();
const std = @import("../std.zig");
const Mutex = std.Thread.Mutex;
const Condition = std.Thread.Condition;
From 8b9434871ea437840d25f073b945466359f402f9 Mon Sep 17 00:00:00 2001
From: Josh Wolfe
Date: Wed, 24 Feb 2021 08:26:13 -0500
Subject: [PATCH 20/20] Avoid concept of a "Unicode character" in documentation
and error messages (#8059)
---
doc/langref.html.in | 30 ++++++++++++++++++++++--------
lib/std/zig/parser_test.zig | 2 +-
lib/std/zig/tokenizer.zig | 6 +++---
src/stage1/tokenizer.cpp | 4 ++--
4 files changed, 28 insertions(+), 14 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index f43abfe1e6..e49609fdbf 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -310,7 +310,7 @@ pub fn main() !void {
The two arguments passed to the stdout.print() function, "Hello, {s}!\n"
and .{"world"}, are evaluated at {#link|compile-time|comptime#}. The code sample is
- purposely written to show how to perform {#link|string|String Literals and Character Literals#}
+ purposely written to show how to perform {#link|string|String Literals and Unicode Code Point Literals#}
substitution in the print function. The curly-braces inside of the first argument
are substituted with the compile-time known value inside of the second argument
(known as an {#link|anonymous struct literal|Anonymous Struct Literals#}). The \n
@@ -682,18 +682,31 @@ pub fn main() void {
{#see_also|Optionals|undefined#}
{#header_close#}
- {#header_open|String Literals and Character Literals#}
+ {#header_open|String Literals and Unicode Code Point Literals#}
- String literals are single-item constant {#link|Pointers#} to null-terminated UTF-8 encoded byte arrays.
+ String literals are single-item constant {#link|Pointers#} to null-terminated byte arrays.
The type of string literals encodes both the length, and the fact that they are null-terminated,
and thus they can be {#link|coerced|Type Coercion#} to both {#link|Slices#} and
{#link|Null-Terminated Pointers|Sentinel-Terminated Pointers#}.
Dereferencing string literals converts them to {#link|Arrays#}.
- Character literals have type {#syntax#}comptime_int{#endsyntax#}, the same as
+ The encoding of a string in Zig is de-facto assumed to be UTF-8.
+ Because Zig source code is {#link|UTF-8 encoded|Source Encoding#}, any non-ASCII bytes appearing within a string literal
+ in source code carry their UTF-8 meaning into the content of the string in the Zig program;
+ the bytes are not modified by the compiler.
+ However, it is possible to embbed non-UTF-8 bytes into a string literal using \xNN notation.
+
+
+ Unicode code point literals have type {#syntax#}comptime_int{#endsyntax#}, the same as
{#link|Integer Literals#}. All {#link|Escape Sequences#} are valid in both string literals
- and character literals.
+ and Unicode code point literals.
+
+
+ In many other programming languages, a Unicode code point literal is called a "character literal".
+ However, there is no precise technical definition of a "character"
+ in recent versions of the Unicode specification (as of Unicode 13.0).
+ In Zig, a Unicode code point literal corresponds to the Unicode definition of a code point.
{#code_begin|test#}
const expect = @import("std").testing.expect;
@@ -709,6 +722,7 @@ test "string literals" {
expect('\u{1f4a9}' == 128169);
expect('💯' == 128175);
expect(mem.eql(u8, "hello", "h\x65llo"));
+ expect("\xff"[0] == 0xff); // non-UTF-8 strings are possible with \xNN notation.
}
{#code_end#}
{#see_also|Arrays|Zig Test|Source Encoding#}
@@ -749,11 +763,11 @@ test "string literals" {
\xNN |
- hexadecimal 8-bit character code (2 digits) |
+ hexadecimal 8-bit byte value (2 digits) |
\u{NNNNNN} |
- hexadecimal Unicode character code UTF-8 encoded (1 or more digits) |
+ hexadecimal Unicode code point UTF-8 encoded (1 or more digits) |
@@ -7414,7 +7428,7 @@ test "main" {
This function returns a compile time constant pointer to null-terminated,
fixed-size array with length equal to the byte count of the file given by
{#syntax#}path{#endsyntax#}. The contents of the array are the contents of the file.
- This is equivalent to a {#link|string literal|String Literals and Character Literals#}
+ This is equivalent to a {#link|string literal|String Literals and Unicode Code Point Literals#}
with the file contents.
diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig
index 822e9006c4..505e900c64 100644
--- a/lib/std/zig/parser_test.zig
+++ b/lib/std/zig/parser_test.zig
@@ -680,7 +680,7 @@ test "zig fmt: enum literal inside array literal" {
);
}
-test "zig fmt: character literal larger than u8" {
+test "zig fmt: Unicode code point literal larger than u8" {
try testCanonical(
\\const x = '\u{01f4a9}';
\\
diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig
index dcbf717638..083f942db6 100644
--- a/lib/std/zig/tokenizer.zig
+++ b/lib/std/zig/tokenizer.zig
@@ -1513,7 +1513,7 @@ test "tokenizer - unknown length pointer and then c pointer" {
});
}
-test "tokenizer - char literal with hex escape" {
+test "tokenizer - code point literal with hex escape" {
testTokenize(
\\'\x1b'
, &[_]Token.Id{.CharLiteral});
@@ -1522,7 +1522,7 @@ test "tokenizer - char literal with hex escape" {
, &[_]Token.Id{ .Invalid, .Invalid });
}
-test "tokenizer - char literal with unicode escapes" {
+test "tokenizer - code point literal with unicode escapes" {
// Valid unicode escapes
testTokenize(
\\'\u{3}'
@@ -1572,7 +1572,7 @@ test "tokenizer - char literal with unicode escapes" {
, &[_]Token.Id{ .Invalid, .IntegerLiteral, .Invalid });
}
-test "tokenizer - char literal with unicode code point" {
+test "tokenizer - code point literal with unicode code point" {
testTokenize(
\\'💩'
, &[_]Token.Id{.CharLiteral});
diff --git a/src/stage1/tokenizer.cpp b/src/stage1/tokenizer.cpp
index 1d25bca17b..623169a313 100644
--- a/src/stage1/tokenizer.cpp
+++ b/src/stage1/tokenizer.cpp
@@ -1447,7 +1447,7 @@ void tokenize(Buf *buf, Tokenization *out) {
tokenize_error(&t, "unterminated string");
break;
} else if (t.cur_tok->id == TokenIdCharLiteral) {
- tokenize_error(&t, "unterminated character literal");
+ tokenize_error(&t, "unterminated Unicode code point literal");
break;
} else {
zig_unreachable();
@@ -1456,7 +1456,7 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateCharLiteral:
case TokenizeStateCharLiteralEnd:
case TokenizeStateCharLiteralUnicode:
- tokenize_error(&t, "unterminated character literal");
+ tokenize_error(&t, "unterminated Unicode code point literal");
break;
case TokenizeStateSymbol:
case TokenizeStateZero: