Merge remote-tracking branch 'origin/master' into llvm7

This commit is contained in:
Andrew Kelley 2018-07-24 00:43:12 -04:00
commit dd9728c5a0
75 changed files with 7546 additions and 1486 deletions

View file

@ -426,6 +426,7 @@ set(ZIG_SOURCES
) )
set(ZIG_CPP_SOURCES set(ZIG_CPP_SOURCES
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
"${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp"
) )
set(ZIG_STD_FILES set(ZIG_STD_FILES
@ -479,6 +480,7 @@ set(ZIG_STD_FILES
"index.zig" "index.zig"
"io.zig" "io.zig"
"json.zig" "json.zig"
"lazy_init.zig"
"linked_list.zig" "linked_list.zig"
"macho.zig" "macho.zig"
"math/acos.zig" "math/acos.zig"
@ -488,6 +490,7 @@ set(ZIG_STD_FILES
"math/atan.zig" "math/atan.zig"
"math/atan2.zig" "math/atan2.zig"
"math/atanh.zig" "math/atanh.zig"
"math/big/index.zig"
"math/big/int.zig" "math/big/int.zig"
"math/cbrt.zig" "math/cbrt.zig"
"math/ceil.zig" "math/ceil.zig"
@ -553,9 +556,10 @@ set(ZIG_STD_FILES
"net.zig" "net.zig"
"os/child_process.zig" "os/child_process.zig"
"os/darwin.zig" "os/darwin.zig"
"os/darwin_errno.zig" "os/darwin/errno.zig"
"os/epoch.zig" "os/epoch.zig"
"os/file.zig" "os/file.zig"
"os/get_app_data_dir.zig"
"os/get_user_id.zig" "os/get_user_id.zig"
"os/index.zig" "os/index.zig"
"os/linux/errno.zig" "os/linux/errno.zig"
@ -564,8 +568,14 @@ set(ZIG_STD_FILES
"os/linux/x86_64.zig" "os/linux/x86_64.zig"
"os/path.zig" "os/path.zig"
"os/time.zig" "os/time.zig"
"os/windows/advapi32.zig"
"os/windows/error.zig" "os/windows/error.zig"
"os/windows/index.zig" "os/windows/index.zig"
"os/windows/kernel32.zig"
"os/windows/ole32.zig"
"os/windows/shell32.zig"
"os/windows/shlwapi.zig"
"os/windows/user32.zig"
"os/windows/util.zig" "os/windows/util.zig"
"os/zen.zig" "os/zen.zig"
"rand/index.zig" "rand/index.zig"
@ -614,6 +624,7 @@ set(ZIG_STD_FILES
"zig/ast.zig" "zig/ast.zig"
"zig/index.zig" "zig/index.zig"
"zig/parse.zig" "zig/parse.zig"
"zig/parse_string_literal.zig"
"zig/render.zig" "zig/render.zig"
"zig/tokenizer.zig" "zig/tokenizer.zig"
) )

View file

@ -21,19 +21,19 @@ clarity.
* Compatible with C libraries with no wrapper necessary. Directly include * Compatible with C libraries with no wrapper necessary. Directly include
C .h files and get access to the functions and symbols therein. C .h files and get access to the functions and symbols therein.
* Provides standard library which competes with the C standard library and is * Provides standard library which competes with the C standard library and is
always compiled against statically in source form. Compile units do not always compiled against statically in source form. Zig binaries do not
depend on libc unless explicitly linked. depend on libc unless explicitly linked.
* Nullable type instead of null pointers. * Optional type instead of null pointers.
* Safe unions, tagged unions, and C ABI compatible unions. * Safe unions, tagged unions, and C ABI compatible unions.
* Generics so that one can write efficient data structures that work for any * Generics so that one can write efficient data structures that work for any
data type. data type.
* No header files required. Top level declarations are entirely * No header files required. Top level declarations are entirely
order-independent. order-independent.
* Compile-time code execution. Compile-time reflection. * Compile-time code execution. Compile-time reflection.
* Partial compile-time function evaluation with eliminates the need for * Partial compile-time function evaluation which eliminates the need for
a preprocessor or macros. a preprocessor or macros.
* The binaries produced by Zig have complete debugging information so you can, * The binaries produced by Zig have complete debugging information so you can,
for example, use GDB or MSVC to debug your software. for example, use GDB, MSVC, or LLDB to debug your software.
* Built-in unit tests with `zig test`. * Built-in unit tests with `zig test`.
* Friendly toward package maintainers. Reproducible build, bootstrapping * Friendly toward package maintainers. Reproducible build, bootstrapping
process carefully documented. Issues filed by package maintainers are process carefully documented. Issues filed by package maintainers are
@ -70,7 +70,7 @@ that counts as "freestanding" for the purposes of this table.
## Community ## Community
* IRC: `#zig` on Freenode. * IRC: `#zig` on Freenode ([Channel Logs](https://irclog.whitequark.org/zig/)).
* Reddit: [/r/zig](https://www.reddit.com/r/zig) * Reddit: [/r/zig](https://www.reddit.com/r/zig)
* Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang) * Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang)

View file

@ -92,7 +92,7 @@ pub fn build(b: *Builder) !void {
test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes)); test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes));
test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); test_step.dependOn(tests.addBuildExampleTests(b, test_filter, modes));
test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));

View file

@ -1087,7 +1087,7 @@ unwrapped == 1234</code></pre>
</td> </td>
<td> <td>
If <code>a</code> is <code>false</code>, returns <code>false</code> If <code>a</code> is <code>false</code>, returns <code>false</code>
without evaluating <code>b</code>. Otherwise, retuns <code>b</code>. without evaluating <code>b</code>. Otherwise, returns <code>b</code>.
</td> </td>
<td> <td>
<pre><code class="zig">false and true == false</code></pre> <pre><code class="zig">false and true == false</code></pre>
@ -1102,7 +1102,7 @@ unwrapped == 1234</code></pre>
</td> </td>
<td> <td>
If <code>a</code> is <code>true</code>, returns <code>true</code> If <code>a</code> is <code>true</code>, returns <code>true</code>
without evaluating <code>b</code>. Otherwise, retuns <code>b</code>. without evaluating <code>b</code>. Otherwise, returns <code>b</code>.
</td> </td>
<td> <td>
<pre><code class="zig">false or true == true</code></pre> <pre><code class="zig">false or true == true</code></pre>
@ -1483,7 +1483,7 @@ test "pointer array access" {
} }
test "pointer slicing" { test "pointer slicing" {
// In Zig, we prefer using slices over null-terminated pointers. // In Zig, we prefer slices over pointers to null-terminated arrays.
// You can turn an array into a slice using slice syntax: // You can turn an array into a slice using slice syntax:
var array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
const slice = array[2..4]; const slice = array[2..4];

View file

@ -4,4 +4,5 @@ pub use @cImport({
@cInclude("inttypes.h"); @cInclude("inttypes.h");
@cInclude("config.h"); @cInclude("config.h");
@cInclude("zig_llvm.h"); @cInclude("zig_llvm.h");
@cInclude("windows_sdk.h");
}); });

68
src-self-hosted/c_int.zig Normal file
View file

@ -0,0 +1,68 @@
pub const CInt = struct {
id: Id,
zig_name: []const u8,
c_name: []const u8,
is_signed: bool,
pub const Id = enum {
Short,
UShort,
Int,
UInt,
Long,
ULong,
LongLong,
ULongLong,
};
pub const list = []CInt{
CInt{
.id = Id.Short,
.zig_name = "c_short",
.c_name = "short",
.is_signed = true,
},
CInt{
.id = Id.UShort,
.zig_name = "c_ushort",
.c_name = "unsigned short",
.is_signed = false,
},
CInt{
.id = Id.Int,
.zig_name = "c_int",
.c_name = "int",
.is_signed = true,
},
CInt{
.id = Id.UInt,
.zig_name = "c_uint",
.c_name = "unsigned int",
.is_signed = false,
},
CInt{
.id = Id.Long,
.zig_name = "c_long",
.c_name = "long",
.is_signed = true,
},
CInt{
.id = Id.ULong,
.zig_name = "c_ulong",
.c_name = "unsigned long",
.is_signed = false,
},
CInt{
.id = Id.LongLong,
.zig_name = "c_longlong",
.c_name = "long long",
.is_signed = true,
},
CInt{
.id = Id.ULongLong,
.zig_name = "c_ulonglong",
.c_name = "unsigned long long",
.is_signed = false,
},
};
};

View file

@ -1,19 +1,22 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin");
const Compilation = @import("compilation.zig").Compilation; const Compilation = @import("compilation.zig").Compilation;
// we go through llvm instead of c for 2 reasons:
// 1. to avoid accidentally calling the non-thread-safe functions
// 2. patch up some of the types to remove nullability
const llvm = @import("llvm.zig"); const llvm = @import("llvm.zig");
const c = @import("c.zig");
const ir = @import("ir.zig"); const ir = @import("ir.zig");
const Value = @import("value.zig").Value; const Value = @import("value.zig").Value;
const Type = @import("type.zig").Type; const Type = @import("type.zig").Type;
const event = std.event; const event = std.event;
const assert = std.debug.assert; const assert = std.debug.assert;
const DW = std.dwarf;
pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void { pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void {
fn_val.base.ref(); fn_val.base.ref();
defer fn_val.base.deref(comp); defer fn_val.base.deref(comp);
defer code.destroy(comp.a()); defer code.destroy(comp.gpa());
var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
errdefer output_path.deinit();
const llvm_handle = try comp.event_loop_local.getAnyLlvmContext(); const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();
defer llvm_handle.release(comp.event_loop_local); defer llvm_handle.release(comp.event_loop_local);
@ -23,15 +26,59 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory; const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory;
defer llvm.DisposeModule(module); defer llvm.DisposeModule(module);
llvm.SetTarget(module, comp.llvm_triple.ptr());
llvm.SetDataLayout(module, comp.target_layout_str);
if (comp.target.getObjectFormat() == builtin.ObjectFormat.coff) {
llvm.AddModuleCodeViewFlag(module);
} else {
llvm.AddModuleDebugInfoFlag(module);
}
const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory; const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory;
defer llvm.DisposeBuilder(builder); defer llvm.DisposeBuilder(builder);
const dibuilder = llvm.CreateDIBuilder(module, true) orelse return error.OutOfMemory;
defer llvm.DisposeDIBuilder(dibuilder);
// Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes
// the git revision.
const producer = try std.Buffer.allocPrint(
&code.arena.allocator,
"zig {}.{}.{}",
u32(c.ZIG_VERSION_MAJOR),
u32(c.ZIG_VERSION_MINOR),
u32(c.ZIG_VERSION_PATCH),
);
const flags = c"";
const runtime_version = 0;
const compile_unit_file = llvm.CreateFile(
dibuilder,
comp.name.ptr(),
comp.root_package.root_src_dir.ptr(),
) orelse return error.OutOfMemory;
const is_optimized = comp.build_mode != builtin.Mode.Debug;
const compile_unit = llvm.CreateCompileUnit(
dibuilder,
DW.LANG_C99,
compile_unit_file,
producer.ptr(),
is_optimized,
flags,
runtime_version,
c"",
0,
!comp.strip,
) orelse return error.OutOfMemory;
var ofile = ObjectFile{ var ofile = ObjectFile{
.comp = comp, .comp = comp,
.module = module, .module = module,
.builder = builder, .builder = builder,
.dibuilder = dibuilder,
.context = context, .context = context,
.lock = event.Lock.init(comp.loop), .lock = event.Lock.init(comp.loop),
.arena = &code.arena.allocator,
}; };
try renderToLlvmModule(&ofile, fn_val, code); try renderToLlvmModule(&ofile, fn_val, code);
@ -41,10 +88,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
// LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm));
//} //}
// TODO llvm.DIBuilderFinalize(dibuilder);
//ZigLLVMDIBuilderFinalize(g->dbuilder);
if (comp.verbose_llvm_ir) { if (comp.verbose_llvm_ir) {
std.debug.warn("raw module:\n");
llvm.DumpModule(ofile.module); llvm.DumpModule(ofile.module);
} }
@ -53,23 +100,56 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
var error_ptr: ?[*]u8 = null; var error_ptr: ?[*]u8 = null;
_ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr); _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr);
} }
assert(comp.emit_file_type == Compilation.Emit.Binary); // TODO support other types
const is_small = comp.build_mode == builtin.Mode.ReleaseSmall;
const is_debug = comp.build_mode == builtin.Mode.Debug;
var err_msg: [*]u8 = undefined;
// TODO integrate this with evented I/O
if (llvm.TargetMachineEmitToFile(
comp.target_machine,
module,
output_path.ptr(),
llvm.EmitBinary,
&err_msg,
is_debug,
is_small,
)) {
if (std.debug.runtime_safety) {
std.debug.panic("unable to write object file {}: {s}\n", output_path.toSliceConst(), err_msg);
}
return error.WritingObjectFileFailed;
}
//validate_inline_fns(g); TODO
fn_val.containing_object = output_path;
if (comp.verbose_llvm_ir) {
std.debug.warn("optimized module:\n");
llvm.DumpModule(ofile.module);
}
if (comp.verbose_link) {
std.debug.warn("created {}\n", output_path.toSliceConst());
}
} }
pub const ObjectFile = struct { pub const ObjectFile = struct {
comp: *Compilation, comp: *Compilation,
module: llvm.ModuleRef, module: llvm.ModuleRef,
builder: llvm.BuilderRef, builder: llvm.BuilderRef,
dibuilder: *llvm.DIBuilder,
context: llvm.ContextRef, context: llvm.ContextRef,
lock: event.Lock, lock: event.Lock,
arena: *std.mem.Allocator,
fn a(self: *ObjectFile) *std.mem.Allocator { fn gpa(self: *ObjectFile) *std.mem.Allocator {
return self.comp.a(); return self.comp.gpa();
} }
}; };
pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void { pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void {
// TODO audit more of codegen.cpp:fn_llvm_value and port more logic // TODO audit more of codegen.cpp:fn_llvm_value and port more logic
const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile); const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction( const llvm_fn = llvm.AddFunction(
ofile.module, ofile.module,
fn_val.symbol_name.ptr(), fn_val.symbol_name.ptr(),
@ -87,7 +167,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
// try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack); // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack);
//} //}
const fn_type = fn_val.base.typeof.cast(Type.Fn).?; const fn_type = fn_val.base.typ.cast(Type.Fn).?;
try addLLVMFnAttr(ofile, llvm_fn, "nounwind"); try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
//add_uwtable_attr(g, fn_table_entry->llvm_value); //add_uwtable_attr(g, fn_table_entry->llvm_value);

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,6 @@ const Allocator = mem.Allocator;
const mem = std.mem; const mem = std.mem;
const ast = std.zig.ast; const ast = std.zig.ast;
const Visib = @import("visib.zig").Visib; const Visib = @import("visib.zig").Visib;
const ParsedFile = @import("parsed_file.zig").ParsedFile;
const event = std.event; const event = std.event;
const Value = @import("value.zig").Value; const Value = @import("value.zig").Value;
const Token = std.zig.Token; const Token = std.zig.Token;
@ -16,8 +15,6 @@ pub const Decl = struct {
name: []const u8, name: []const u8,
visib: Visib, visib: Visib,
resolution: event.Future(Compilation.BuildError!void), resolution: event.Future(Compilation.BuildError!void),
resolution_in_progress: u8,
parsed_file: *ParsedFile,
parent_scope: *Scope, parent_scope: *Scope,
pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
@ -48,6 +45,10 @@ pub const Decl = struct {
} }
} }
pub fn findRootScope(base: *const Decl) *Scope.Root {
return base.parent_scope.findRoot();
}
pub const Id = enum { pub const Id = enum {
Var, Var,
Fn, Fn,
@ -61,12 +62,13 @@ pub const Decl = struct {
pub const Fn = struct { pub const Fn = struct {
base: Decl, base: Decl,
value: Val, value: Val,
fn_proto: *const ast.Node.FnProto, fn_proto: *ast.Node.FnProto,
// TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
pub const Val = union { pub const Val = union(enum) {
Unresolved: void, Unresolved: void,
Ok: *Value.Fn, Fn: *Value.Fn,
FnProto: *Value.FnProto,
}; };
pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {

View file

@ -4,6 +4,8 @@ const os = std.os;
const Token = std.zig.Token; const Token = std.zig.Token;
const ast = std.zig.ast; const ast = std.zig.ast;
const TokenIndex = std.zig.ast.TokenIndex; const TokenIndex = std.zig.ast.TokenIndex;
const Compilation = @import("compilation.zig").Compilation;
const Scope = @import("scope.zig").Scope;
pub const Color = enum { pub const Color = enum {
Auto, Auto,
@ -16,85 +18,220 @@ pub const Span = struct {
last: ast.TokenIndex, last: ast.TokenIndex,
pub fn token(i: TokenIndex) Span { pub fn token(i: TokenIndex) Span {
return Span { return Span{
.first = i, .first = i,
.last = i, .last = i,
}; };
} }
pub fn node(n: *ast.Node) Span {
return Span{
.first = n.firstToken(),
.last = n.lastToken(),
};
}
}; };
pub const Msg = struct { pub const Msg = struct {
path: []const u8,
text: []u8,
span: Span, span: Span,
tree: *ast.Tree, text: []u8,
}; data: Data,
/// `path` must outlive the returned Msg const Data = union(enum) {
/// `tree` must outlive the returned Msg PathAndTree: PathAndTree,
/// Caller owns returned Msg and must free with `allocator` ScopeAndComp: ScopeAndComp,
pub fn createFromParseError( };
allocator: *mem.Allocator,
parse_error: *const ast.Error,
tree: *ast.Tree,
path: []const u8,
) !*Msg {
const loc_token = parse_error.loc();
var text_buf = try std.Buffer.initSize(allocator, 0);
defer text_buf.deinit();
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; const PathAndTree = struct {
try parse_error.render(&tree.tokens, out_stream); realpath: []const u8,
tree: *ast.Tree,
allocator: *mem.Allocator,
};
const msg = try allocator.create(Msg{ const ScopeAndComp = struct {
.tree = tree, root_scope: *Scope.Root,
.path = path, compilation: *Compilation,
.text = text_buf.toOwnedSlice(), };
.span = Span{
.first = loc_token,
.last = loc_token,
},
});
errdefer allocator.destroy(msg);
return msg; pub fn destroy(self: *Msg) void {
} switch (self.data) {
Data.PathAndTree => |path_and_tree| {
path_and_tree.allocator.free(self.text);
path_and_tree.allocator.destroy(self);
},
Data.ScopeAndComp => |scope_and_comp| {
scope_and_comp.root_scope.base.deref(scope_and_comp.compilation);
scope_and_comp.compilation.gpa().free(self.text);
scope_and_comp.compilation.gpa().destroy(self);
},
}
}
fn getAllocator(self: *const Msg) *mem.Allocator {
switch (self.data) {
Data.PathAndTree => |path_and_tree| {
return path_and_tree.allocator;
},
Data.ScopeAndComp => |scope_and_comp| {
return scope_and_comp.compilation.gpa();
},
}
}
pub fn getRealPath(self: *const Msg) []const u8 {
switch (self.data) {
Data.PathAndTree => |path_and_tree| {
return path_and_tree.realpath;
},
Data.ScopeAndComp => |scope_and_comp| {
return scope_and_comp.root_scope.realpath;
},
}
}
pub fn getTree(self: *const Msg) *ast.Tree {
switch (self.data) {
Data.PathAndTree => |path_and_tree| {
return path_and_tree.tree;
},
Data.ScopeAndComp => |scope_and_comp| {
return scope_and_comp.root_scope.tree;
},
}
}
/// Takes ownership of text
/// References root_scope, and derefs when the msg is freed
pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {
const msg = try comp.gpa().create(Msg{
.text = text,
.span = span,
.data = Data{
.ScopeAndComp = ScopeAndComp{
.root_scope = root_scope,
.compilation = comp,
},
},
});
root_scope.base.ref();
return msg;
}
pub fn createFromParseErrorAndScope(
comp: *Compilation,
root_scope: *Scope.Root,
parse_error: *const ast.Error,
) !*Msg {
const loc_token = parse_error.loc();
var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
defer text_buf.deinit();
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
try parse_error.render(&root_scope.tree.tokens, out_stream);
const msg = try comp.gpa().create(Msg{
.text = undefined,
.span = Span{
.first = loc_token,
.last = loc_token,
},
.data = Data{
.ScopeAndComp = ScopeAndComp{
.root_scope = root_scope,
.compilation = comp,
},
},
});
root_scope.base.ref();
msg.text = text_buf.toOwnedSlice();
return msg;
}
/// `realpath` must outlive the returned Msg
/// `tree` must outlive the returned Msg
/// Caller owns returned Msg and must free with `allocator`
/// allocator will additionally be used for printing messages later.
pub fn createFromParseError(
allocator: *mem.Allocator,
parse_error: *const ast.Error,
tree: *ast.Tree,
realpath: []const u8,
) !*Msg {
const loc_token = parse_error.loc();
var text_buf = try std.Buffer.initSize(allocator, 0);
defer text_buf.deinit();
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
try parse_error.render(&tree.tokens, out_stream);
const msg = try allocator.create(Msg{
.text = undefined,
.data = Data{
.PathAndTree = PathAndTree{
.allocator = allocator,
.realpath = realpath,
.tree = tree,
},
},
.span = Span{
.first = loc_token,
.last = loc_token,
},
});
msg.text = text_buf.toOwnedSlice();
errdefer allocator.destroy(msg);
return msg;
}
pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
const allocator = msg.getAllocator();
const realpath = msg.getRealPath();
const tree = msg.getTree();
const cwd = try os.getCwd(allocator);
defer allocator.free(cwd);
const relpath = try os.path.relative(allocator, cwd, realpath);
defer allocator.free(relpath);
const path = if (relpath.len < realpath.len) relpath else realpath;
const first_token = tree.tokens.at(msg.span.first);
const last_token = tree.tokens.at(msg.span.last);
const start_loc = tree.tokenLocationPtr(0, first_token);
const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
if (!color_on) {
try stream.print(
"{}:{}:{}: error: {}\n",
path,
start_loc.line + 1,
start_loc.column + 1,
msg.text,
);
return;
}
pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void {
const first_token = msg.tree.tokens.at(msg.span.first);
const last_token = msg.tree.tokens.at(msg.span.last);
const start_loc = msg.tree.tokenLocationPtr(0, first_token);
const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token);
if (!color_on) {
try stream.print( try stream.print(
"{}:{}:{}: error: {}\n", "{}:{}:{}: error: {}\n{}\n",
msg.path, path,
start_loc.line + 1, start_loc.line + 1,
start_loc.column + 1, start_loc.column + 1,
msg.text, msg.text,
tree.source[start_loc.line_start..start_loc.line_end],
); );
return; try stream.writeByteNTimes(' ', start_loc.column);
try stream.writeByteNTimes('~', last_token.end - first_token.start);
try stream.write("\n");
} }
try stream.print( pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void {
"{}:{}:{}: error: {}\n{}\n", const color_on = switch (color) {
msg.path, Color.Auto => file.isTty(),
start_loc.line + 1, Color.On => true,
start_loc.column + 1, Color.Off => false,
msg.text, };
msg.tree.source[start_loc.line_start..start_loc.line_end], var stream = &std.io.FileOutStream.init(file).stream;
); return msg.printToStream(stream, color_on);
try stream.writeByteNTimes(' ', start_loc.column); }
try stream.writeByteNTimes('~', last_token.end - first_token.start); };
try stream.write("\n");
}
pub fn printToFile(file: *os.File, msg: *const Msg, color: Color) !void {
const color_on = switch (color) {
Color.Auto => file.isTty(),
Color.On => true,
Color.Off => false,
};
var stream = &std.io.FileOutStream.init(file).stream;
return printToStream(stream, msg, color_on);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,462 @@
const std = @import("std");
const builtin = @import("builtin");
const event = std.event;
const Target = @import("target.zig").Target;
const c = @import("c.zig");
/// See the render function implementation for documentation of the fields.
pub const LibCInstallation = struct {
include_dir: []const u8,
lib_dir: ?[]const u8,
static_lib_dir: ?[]const u8,
msvc_lib_dir: ?[]const u8,
kernel32_lib_dir: ?[]const u8,
dynamic_linker_path: ?[]const u8,
pub const FindError = error{
OutOfMemory,
FileSystem,
UnableToSpawnCCompiler,
CCompilerExitCode,
CCompilerCrashed,
CCompilerCannotFindHeaders,
LibCRuntimeNotFound,
LibCStdLibHeaderNotFound,
LibCKernel32LibNotFound,
UnsupportedArchitecture,
};
pub fn parse(
self: *LibCInstallation,
allocator: *std.mem.Allocator,
libc_file: []const u8,
stderr: *std.io.OutStream(std.io.FileOutStream.Error),
) !void {
self.initEmpty();
const keys = []const []const u8{
"include_dir",
"lib_dir",
"static_lib_dir",
"msvc_lib_dir",
"kernel32_lib_dir",
"dynamic_linker_path",
};
const FoundKey = struct {
found: bool,
allocated: ?[]u8,
};
var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** keys.len;
errdefer {
self.initEmpty();
for (found_keys) |found_key| {
if (found_key.allocated) |s| allocator.free(s);
}
}
const contents = try std.io.readFileAlloc(allocator, libc_file);
defer allocator.free(contents);
var it = std.mem.split(contents, "\n");
while (it.next()) |line| {
if (line.len == 0 or line[0] == '#') continue;
var line_it = std.mem.split(line, "=");
const name = line_it.next() orelse {
try stderr.print("missing equal sign after field name\n");
return error.ParseError;
};
const value = line_it.rest();
inline for (keys) |key, i| {
if (std.mem.eql(u8, name, key)) {
found_keys[i].found = true;
switch (@typeInfo(@typeOf(@field(self, key)))) {
builtin.TypeId.Optional => {
if (value.len == 0) {
@field(self, key) = null;
} else {
found_keys[i].allocated = try std.mem.dupe(allocator, u8, value);
@field(self, key) = found_keys[i].allocated;
}
},
else => {
if (value.len == 0) {
try stderr.print("field cannot be empty: {}\n", key);
return error.ParseError;
}
const dupe = try std.mem.dupe(allocator, u8, value);
found_keys[i].allocated = dupe;
@field(self, key) = dupe;
},
}
break;
}
}
}
for (found_keys) |found_key, i| {
if (!found_key.found) {
try stderr.print("missing field: {}\n", keys[i]);
return error.ParseError;
}
}
}
pub fn render(self: *const LibCInstallation, out: *std.io.OutStream(std.io.FileOutStream.Error)) !void {
@setEvalBranchQuota(4000);
try out.print(
\\# The directory that contains `stdlib.h`.
\\# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null`
\\include_dir={}
\\
\\# The directory that contains `crt1.o`.
\\# On Linux, can be found with `cc -print-file-name=crt1.o`.
\\# Not needed when targeting MacOS.
\\lib_dir={}
\\
\\# The directory that contains `crtbegin.o`.
\\# On Linux, can be found with `cc -print-file-name=crtbegin.o`.
\\# Not needed when targeting MacOS or Windows.
\\static_lib_dir={}
\\
\\# The directory that contains `vcruntime.lib`.
\\# Only needed when targeting Windows.
\\msvc_lib_dir={}
\\
\\# The directory that contains `kernel32.lib`.
\\# Only needed when targeting Windows.
\\kernel32_lib_dir={}
\\
\\# The full path to the dynamic linker, on the target system.
\\# Only needed when targeting Linux.
\\dynamic_linker_path={}
\\
,
self.include_dir,
self.lib_dir orelse "",
self.static_lib_dir orelse "",
self.msvc_lib_dir orelse "",
self.kernel32_lib_dir orelse "",
self.dynamic_linker_path orelse Target(Target.Native).getDynamicLinkerPath(),
);
}
/// Finds the default, native libc.
pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
self.initEmpty();
var group = event.Group(FindError!void).init(loop);
errdefer group.cancelAll();
var windows_sdk: ?*c.ZigWindowsSDK = null;
errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));
switch (builtin.os) {
builtin.Os.windows => {
var sdk: *c.ZigWindowsSDK = undefined;
switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) {
c.ZigFindWindowsSdkError.None => {
windows_sdk = sdk;
if (sdk.msvc_lib_dir_ptr) |ptr| {
self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]);
}
try group.call(findNativeKernel32LibDir, self, loop, sdk);
try group.call(findNativeIncludeDirWindows, self, loop, sdk);
try group.call(findNativeLibDirWindows, self, loop, sdk);
},
c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory,
c.ZigFindWindowsSdkError.NotFound => return error.NotFound,
c.ZigFindWindowsSdkError.PathTooLong => return error.NotFound,
}
},
builtin.Os.linux => {
try group.call(findNativeIncludeDirLinux, self, loop);
try group.call(findNativeLibDirLinux, self, loop);
try group.call(findNativeStaticLibDir, self, loop);
try group.call(findNativeDynamicLinker, self, loop);
},
builtin.Os.macosx => {
self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include");
},
else => @compileError("unimplemented: find libc for this OS"),
}
return await (async group.wait() catch unreachable);
}
async fn findNativeIncludeDirLinux(self: *LibCInstallation, loop: *event.Loop) !void {
const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
const argv = []const []const u8{
cc_exe,
"-E",
"-Wp,-v",
"-xc",
"/dev/null",
};
// TODO make this use event loop
const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
const exec_result = if (std.debug.runtime_safety) blk: {
break :blk errorable_result catch unreachable;
} else blk: {
break :blk errorable_result catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => return error.UnableToSpawnCCompiler,
};
};
defer {
loop.allocator.free(exec_result.stdout);
loop.allocator.free(exec_result.stderr);
}
switch (exec_result.term) {
std.os.ChildProcess.Term.Exited => |code| {
if (code != 0) return error.CCompilerExitCode;
},
else => {
return error.CCompilerCrashed;
},
}
var it = std.mem.split(exec_result.stderr, "\n\r");
var search_paths = std.ArrayList([]const u8).init(loop.allocator);
defer search_paths.deinit();
while (it.next()) |line| {
if (line.len != 0 and line[0] == ' ') {
try search_paths.append(line);
}
}
if (search_paths.len == 0) {
return error.CCompilerCannotFindHeaders;
}
// search in reverse order
var path_i: usize = 0;
while (path_i < search_paths.len) : (path_i += 1) {
const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");
defer loop.allocator.free(stdlib_path);
if (try fileExists(loop.allocator, stdlib_path)) {
self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path);
return;
}
}
return error.LibCStdLibHeaderNotFound;
}
async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) !void {
var search_buf: [2]Search = undefined;
const searches = fillSearch(&search_buf, sdk);
var result_buf = try std.Buffer.initSize(loop.allocator, 0);
defer result_buf.deinit();
for (searches) |search| {
result_buf.shrink(0);
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version);
const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");
defer loop.allocator.free(stdlib_path);
if (try fileExists(loop.allocator, stdlib_path)) {
self.include_dir = result_buf.toOwnedSlice();
return;
}
}
return error.LibCStdLibHeaderNotFound;
}
async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
var search_buf: [2]Search = undefined;
const searches = fillSearch(&search_buf, sdk);
var result_buf = try std.Buffer.initSize(loop.allocator, 0);
defer result_buf.deinit();
for (searches) |search| {
result_buf.shrink(0);
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
try stream.print("{}\\Lib\\{}\\ucrt\\", search.path, search.version);
switch (builtin.arch) {
builtin.Arch.i386 => try stream.write("x86"),
builtin.Arch.x86_64 => try stream.write("x64"),
builtin.Arch.aarch64 => try stream.write("arm"),
else => return error.UnsupportedArchitecture,
}
const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");
defer loop.allocator.free(ucrt_lib_path);
if (try fileExists(loop.allocator, ucrt_lib_path)) {
self.lib_dir = result_buf.toOwnedSlice();
return;
}
}
return error.LibCRuntimeNotFound;
}
async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) FindError!void {
self.lib_dir = try await (async ccPrintFileName(loop, "crt1.o", true) catch unreachable);
}
async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void {
self.static_lib_dir = try await (async ccPrintFileName(loop, "crtbegin.o", true) catch unreachable);
}
async fn findNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop) FindError!void {
var dyn_tests = []DynTest{
DynTest{
.name = "ld-linux-x86-64.so.2",
.result = null,
},
DynTest{
.name = "ld-musl-x86_64.so.1",
.result = null,
},
};
var group = event.Group(FindError!void).init(loop);
errdefer group.cancelAll();
for (dyn_tests) |*dyn_test| {
try group.call(testNativeDynamicLinker, self, loop, dyn_test);
}
try await (async group.wait() catch unreachable);
for (dyn_tests) |*dyn_test| {
if (dyn_test.result) |result| {
self.dynamic_linker_path = result;
return;
}
}
}
const DynTest = struct {
name: []const u8,
result: ?[]const u8,
};
async fn testNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop, dyn_test: *DynTest) FindError!void {
if (await (async ccPrintFileName(loop, dyn_test.name, false) catch unreachable)) |result| {
dyn_test.result = result;
return;
} else |err| switch (err) {
error.LibCRuntimeNotFound => return,
else => return err,
}
}
async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
var search_buf: [2]Search = undefined;
const searches = fillSearch(&search_buf, sdk);
var result_buf = try std.Buffer.initSize(loop.allocator, 0);
defer result_buf.deinit();
for (searches) |search| {
result_buf.shrink(0);
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
try stream.print("{}\\Lib\\{}\\um\\", search.path, search.version);
switch (builtin.arch) {
builtin.Arch.i386 => try stream.write("x86\\"),
builtin.Arch.x86_64 => try stream.write("x64\\"),
builtin.Arch.aarch64 => try stream.write("arm\\"),
else => return error.UnsupportedArchitecture,
}
const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");
defer loop.allocator.free(kernel32_path);
if (try fileExists(loop.allocator, kernel32_path)) {
self.kernel32_lib_dir = result_buf.toOwnedSlice();
return;
}
}
return error.LibCKernel32LibNotFound;
}
fn initEmpty(self: *LibCInstallation) void {
self.* = LibCInstallation{
.include_dir = ([*]const u8)(undefined)[0..0],
.lib_dir = null,
.static_lib_dir = null,
.msvc_lib_dir = null,
.kernel32_lib_dir = null,
.dynamic_linker_path = null,
};
}
};
/// caller owns returned memory
async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bool) ![]u8 {
const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
const arg1 = try std.fmt.allocPrint(loop.allocator, "-print-file-name={}", o_file);
defer loop.allocator.free(arg1);
const argv = []const []const u8{ cc_exe, arg1 };
// TODO This simulates evented I/O for the child process exec
await (async loop.yield() catch unreachable);
const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
const exec_result = if (std.debug.runtime_safety) blk: {
break :blk errorable_result catch unreachable;
} else blk: {
break :blk errorable_result catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => return error.UnableToSpawnCCompiler,
};
};
defer {
loop.allocator.free(exec_result.stdout);
loop.allocator.free(exec_result.stderr);
}
switch (exec_result.term) {
std.os.ChildProcess.Term.Exited => |code| {
if (code != 0) return error.CCompilerExitCode;
},
else => {
return error.CCompilerCrashed;
},
}
var it = std.mem.split(exec_result.stdout, "\n\r");
const line = it.next() orelse return error.LibCRuntimeNotFound;
const dirname = std.os.path.dirname(line) orelse return error.LibCRuntimeNotFound;
if (want_dirname) {
return std.mem.dupe(loop.allocator, u8, dirname);
} else {
return std.mem.dupe(loop.allocator, u8, line);
}
}
const Search = struct {
path: []const u8,
version: []const u8,
};
fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
var search_end: usize = 0;
if (sdk.path10_ptr) |path10_ptr| {
if (sdk.version10_ptr) |ver10_ptr| {
search_buf[search_end] = Search{
.path = path10_ptr[0..sdk.path10_len],
.version = ver10_ptr[0..sdk.version10_len],
};
search_end += 1;
}
}
if (sdk.path81_ptr) |path81_ptr| {
if (sdk.version81_ptr) |ver81_ptr| {
search_buf[search_end] = Search{
.path = path81_ptr[0..sdk.path81_len],
.version = ver81_ptr[0..sdk.version81_len],
};
search_end += 1;
}
}
return search_buf[0..search_end];
}
fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {
if (std.os.File.access(allocator, path)) |_| {
return true;
} else |err| switch (err) {
error.NotFound, error.PermissionDenied => return false,
error.OutOfMemory => return error.OutOfMemory,
else => return error.FileSystem,
}
}

724
src-self-hosted/link.zig Normal file
View file

@ -0,0 +1,724 @@
const std = @import("std");
const mem = std.mem;
const c = @import("c.zig");
const builtin = @import("builtin");
const ObjectFormat = builtin.ObjectFormat;
const Compilation = @import("compilation.zig").Compilation;
const Target = @import("target.zig").Target;
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
const assert = std.debug.assert;
const Context = struct {
comp: *Compilation,
arena: std.heap.ArenaAllocator,
args: std.ArrayList([*]const u8),
link_in_crt: bool,
link_err: error{OutOfMemory}!void,
link_msg: std.Buffer,
libc: *LibCInstallation,
out_file_path: std.Buffer,
};
pub async fn link(comp: *Compilation) !void {
var ctx = Context{
.comp = comp,
.arena = std.heap.ArenaAllocator.init(comp.gpa()),
.args = undefined,
.link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe,
.link_err = {},
.link_msg = undefined,
.libc = undefined,
.out_file_path = undefined,
};
defer ctx.arena.deinit();
ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator);
ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
if (comp.link_out_file) |out_file| {
ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, out_file);
} else {
ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, comp.name.toSliceConst());
switch (comp.kind) {
Compilation.Kind.Exe => {
try ctx.out_file_path.append(comp.target.exeFileExt());
},
Compilation.Kind.Lib => {
try ctx.out_file_path.append(comp.target.libFileExt(comp.is_static));
},
Compilation.Kind.Obj => {
try ctx.out_file_path.append(comp.target.objFileExt());
},
}
}
// even though we're calling LLD as a library it thinks the first
// argument is its own exe name
try ctx.args.append(c"lld");
if (comp.haveLibC()) {
ctx.libc = ctx.comp.override_libc orelse blk: {
switch (comp.target) {
Target.Native => {
break :blk (await (async comp.event_loop_local.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
},
else => return error.LibCRequiredButNotProvidedOrFound,
}
};
}
try constructLinkerArgs(&ctx);
if (comp.verbose_link) {
for (ctx.args.toSliceConst()) |arg, i| {
const space = if (i == 0) "" else " ";
std.debug.warn("{}{s}", space, arg);
}
std.debug.warn("\n");
}
const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
const args_slice = ctx.args.toSlice();
// Not evented I/O. LLD does its own multithreading internally.
if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
if (!ctx.link_msg.isNull()) {
// TODO capture these messages and pass them through the system, reporting them through the
// event system instead of printing them directly here.
// perhaps try to parse and understand them.
std.debug.warn("{}\n", ctx.link_msg.toSliceConst());
}
return error.LinkFailed;
}
}
extern fn ZigLLDLink(
oformat: c.ZigLLVM_ObjectFormatType,
args: [*]const [*]const u8,
arg_count: usize,
append_diagnostic: extern fn (*c_void, [*]const u8, usize) void,
context: *c_void,
) bool;
extern fn linkDiagCallback(context: *c_void, ptr: [*]const u8, len: usize) void {
const ctx = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
ctx.link_err = linkDiagCallbackErrorable(ctx, ptr[0..len]);
}
fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void {
if (ctx.link_msg.isNull()) {
try ctx.link_msg.resize(0);
}
try ctx.link_msg.append(msg);
}
fn toExternObjectFormatType(ofmt: ObjectFormat) c.ZigLLVM_ObjectFormatType {
return switch (ofmt) {
ObjectFormat.unknown => c.ZigLLVM_UnknownObjectFormat,
ObjectFormat.coff => c.ZigLLVM_COFF,
ObjectFormat.elf => c.ZigLLVM_ELF,
ObjectFormat.macho => c.ZigLLVM_MachO,
ObjectFormat.wasm => c.ZigLLVM_Wasm,
};
}
fn constructLinkerArgs(ctx: *Context) !void {
switch (ctx.comp.target.getObjectFormat()) {
ObjectFormat.unknown => unreachable,
ObjectFormat.coff => return constructLinkerArgsCoff(ctx),
ObjectFormat.elf => return constructLinkerArgsElf(ctx),
ObjectFormat.macho => return constructLinkerArgsMachO(ctx),
ObjectFormat.wasm => return constructLinkerArgsWasm(ctx),
}
}
fn constructLinkerArgsElf(ctx: *Context) !void {
// TODO commented out code in this function
//if (g->linker_script) {
// lj->args.append("-T");
// lj->args.append(g->linker_script);
//}
//if (g->no_rosegment_workaround) {
// lj->args.append("--no-rosegment");
//}
try ctx.args.append(c"--gc-sections");
//lj->args.append("-m");
//lj->args.append(getLDMOption(&g->zig_target));
//bool is_lib = g->out_type == OutTypeLib;
//bool shared = !g->is_static && is_lib;
//Buf *soname = nullptr;
if (ctx.comp.is_static) {
if (ctx.comp.target.isArmOrThumb()) {
try ctx.args.append(c"-Bstatic");
} else {
try ctx.args.append(c"-static");
}
}
//} else if (shared) {
// lj->args.append("-shared");
// if (buf_len(&lj->out_file) == 0) {
// buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "",
// buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
// }
// soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major);
//}
try ctx.args.append(c"-o");
try ctx.args.append(ctx.out_file_path.ptr());
if (ctx.link_in_crt) {
const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
const crtbegino = if (ctx.comp.is_static) "crtbeginT.o" else "crtbegin.o";
try addPathJoin(ctx, ctx.libc.lib_dir.?, crt1o);
try addPathJoin(ctx, ctx.libc.lib_dir.?, "crti.o");
try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino);
}
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
// Buf *rpath = g->rpath_list.at(i);
// add_rpath(lj, rpath);
//}
//if (g->each_lib_rpath) {
// for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
// const char *lib_dir = g->lib_dirs.at(i);
// for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
// LinkLib *link_lib = g->link_libs_list.at(i);
// if (buf_eql_str(link_lib->name, "c")) {
// continue;
// }
// bool does_exist;
// Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name));
// if (os_file_exists(test_path, &does_exist) != ErrorNone) {
// zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path));
// }
// if (does_exist) {
// add_rpath(lj, buf_create_from_str(lib_dir));
// break;
// }
// }
// }
//}
//for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
// const char *lib_dir = g->lib_dirs.at(i);
// lj->args.append("-L");
// lj->args.append(lib_dir);
//}
if (ctx.comp.haveLibC()) {
try ctx.args.append(c"-L");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr);
try ctx.args.append(c"-L");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr);
if (!ctx.comp.is_static) {
const dl = blk: {
if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
if (ctx.comp.target.getDynamicLinkerPath()) |dl| break :blk dl;
return error.LibCMissingDynamicLinker;
};
try ctx.args.append(c"-dynamic-linker");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr);
}
}
//if (shared) {
// lj->args.append("-soname");
// lj->args.append(buf_ptr(soname));
//}
// .o files
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
try ctx.args.append(link_obj_with_null.ptr);
}
try addFnObjects(ctx);
//if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
// if (g->libc_link_lib == nullptr) {
// Buf *builtin_o_path = build_o(g, "builtin");
// lj->args.append(buf_ptr(builtin_o_path));
// }
// // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage
// Buf *compiler_rt_o_path = build_compiler_rt(g);
// lj->args.append(buf_ptr(compiler_rt_o_path));
//}
//for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
// LinkLib *link_lib = g->link_libs_list.at(i);
// if (buf_eql_str(link_lib->name, "c")) {
// continue;
// }
// Buf *arg;
// if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") ||
// buf_ends_with_str(link_lib->name, ".so"))
// {
// arg = link_lib->name;
// } else {
// arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
// }
// lj->args.append(buf_ptr(arg));
//}
// libc dep
if (ctx.comp.haveLibC()) {
if (ctx.comp.is_static) {
try ctx.args.append(c"--start-group");
try ctx.args.append(c"-lgcc");
try ctx.args.append(c"-lgcc_eh");
try ctx.args.append(c"-lc");
try ctx.args.append(c"-lm");
try ctx.args.append(c"--end-group");
} else {
try ctx.args.append(c"-lgcc");
try ctx.args.append(c"--as-needed");
try ctx.args.append(c"-lgcc_s");
try ctx.args.append(c"--no-as-needed");
try ctx.args.append(c"-lc");
try ctx.args.append(c"-lm");
try ctx.args.append(c"-lgcc");
try ctx.args.append(c"--as-needed");
try ctx.args.append(c"-lgcc_s");
try ctx.args.append(c"--no-as-needed");
}
}
// crt end
if (ctx.link_in_crt) {
try addPathJoin(ctx, ctx.libc.static_lib_dir.?, "crtend.o");
try addPathJoin(ctx, ctx.libc.lib_dir.?, "crtn.o");
}
if (ctx.comp.target != Target.Native) {
try ctx.args.append(c"--allow-shlib-undefined");
}
if (ctx.comp.target.getOs() == builtin.Os.zen) {
try ctx.args.append(c"-e");
try ctx.args.append(c"_start");
try ctx.args.append(c"--image-base=0x10000000");
}
}
fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename);
const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
try ctx.args.append(full_path_with_null.ptr);
}
fn constructLinkerArgsCoff(ctx: *Context) !void {
try ctx.args.append(c"-NOLOGO");
if (!ctx.comp.strip) {
try ctx.args.append(c"-DEBUG");
}
switch (ctx.comp.target.getArch()) {
builtin.Arch.i386 => try ctx.args.append(c"-MACHINE:X86"),
builtin.Arch.x86_64 => try ctx.args.append(c"-MACHINE:X64"),
builtin.Arch.aarch64 => try ctx.args.append(c"-MACHINE:ARM"),
else => return error.UnsupportedLinkArchitecture,
}
if (ctx.comp.windows_subsystem_windows) {
try ctx.args.append(c"/SUBSYSTEM:windows");
} else if (ctx.comp.windows_subsystem_console) {
try ctx.args.append(c"/SUBSYSTEM:console");
}
const is_library = ctx.comp.kind == Compilation.Kind.Lib;
const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst());
try ctx.args.append(out_arg.ptr);
if (ctx.comp.haveLibC()) {
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr);
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr);
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr);
}
if (ctx.link_in_crt) {
const lib_str = if (ctx.comp.is_static) "lib" else "";
const d_str = if (ctx.comp.build_mode == builtin.Mode.Debug) "d" else "";
if (ctx.comp.is_static) {
const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str);
try ctx.args.append(cmt_lib_name.ptr);
} else {
const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str);
try ctx.args.append(msvcrt_lib_name.ptr);
}
const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str);
try ctx.args.append(vcruntime_lib_name.ptr);
const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str);
try ctx.args.append(crt_lib_name.ptr);
// Visual C++ 2015 Conformance Changes
// https://msdn.microsoft.com/en-us/library/bb531344.aspx
try ctx.args.append(c"legacy_stdio_definitions.lib");
// msvcrt depends on kernel32
try ctx.args.append(c"kernel32.lib");
} else {
try ctx.args.append(c"-NODEFAULTLIB");
if (!is_library) {
try ctx.args.append(c"-ENTRY:WinMainCRTStartup");
// TODO
//if (g->have_winmain) {
// lj->args.append("-ENTRY:WinMain");
//} else {
// lj->args.append("-ENTRY:WinMainCRTStartup");
//}
}
}
if (is_library and !ctx.comp.is_static) {
try ctx.args.append(c"-DLL");
}
//for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
// const char *lib_dir = g->lib_dirs.at(i);
// lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir)));
//}
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
try ctx.args.append(link_obj_with_null.ptr);
}
try addFnObjects(ctx);
switch (ctx.comp.kind) {
Compilation.Kind.Exe, Compilation.Kind.Lib => {
if (!ctx.comp.haveLibC()) {
@panic("TODO");
//Buf *builtin_o_path = build_o(g, "builtin");
//lj->args.append(buf_ptr(builtin_o_path));
}
// msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage
// TODO
//Buf *compiler_rt_o_path = build_compiler_rt(g);
//lj->args.append(buf_ptr(compiler_rt_o_path));
},
Compilation.Kind.Obj => {},
}
//Buf *def_contents = buf_alloc();
//ZigList<const char *> gen_lib_args = {0};
//for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) {
// LinkLib *link_lib = g->link_libs_list.at(lib_i);
// if (buf_eql_str(link_lib->name, "c")) {
// continue;
// }
// if (link_lib->provided_explicitly) {
// if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) {
// Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
// lj->args.append(buf_ptr(arg));
// }
// else {
// lj->args.append(buf_ptr(link_lib->name));
// }
// } else {
// buf_resize(def_contents, 0);
// buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name));
// for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) {
// Buf *symbol_name = link_lib->symbols.at(exp_i);
// buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name));
// }
// buf_appendf(def_contents, "\n");
// Buf *def_path = buf_alloc();
// os_path_join(g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path);
// os_write_file(def_path, def_contents);
// Buf *generated_lib_path = buf_alloc();
// os_path_join(g->cache_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path);
// gen_lib_args.resize(0);
// gen_lib_args.append("link");
// coff_append_machine_arg(g, &gen_lib_args);
// gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path))));
// gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path))));
// Buf diag = BUF_INIT;
// if (!zig_lld_link(g->zig_target.oformat, gen_lib_args.items, gen_lib_args.length, &diag)) {
// fprintf(stderr, "%s\n", buf_ptr(&diag));
// exit(1);
// }
// lj->args.append(buf_ptr(generated_lib_path));
// }
//}
}
fn constructLinkerArgsMachO(ctx: *Context) !void {
try ctx.args.append(c"-demangle");
if (ctx.comp.linker_rdynamic) {
try ctx.args.append(c"-export_dynamic");
}
const is_lib = ctx.comp.kind == Compilation.Kind.Lib;
const shared = !ctx.comp.is_static and is_lib;
if (ctx.comp.is_static) {
try ctx.args.append(c"-static");
} else {
try ctx.args.append(c"-dynamic");
}
//if (is_lib) {
// if (!g->is_static) {
// lj->args.append("-dylib");
// Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major);
// lj->args.append("-compatibility_version");
// lj->args.append(buf_ptr(compat_vers));
// Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize,
// g->version_major, g->version_minor, g->version_patch);
// lj->args.append("-current_version");
// lj->args.append(buf_ptr(cur_vers));
// // TODO getting an error when running an executable when doing this rpath thing
// //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
// // buf_ptr(g->root_out_name), g->version_major);
// //lj->args.append("-install_name");
// //lj->args.append(buf_ptr(dylib_install_name));
// if (buf_len(&lj->out_file) == 0) {
// buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib",
// buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
// }
// }
//}
try ctx.args.append(c"-arch");
const darwin_arch_str = try std.cstr.addNullByte(
&ctx.arena.allocator,
ctx.comp.target.getDarwinArchString(),
);
try ctx.args.append(darwin_arch_str.ptr);
const platform = try DarwinPlatform.get(ctx.comp);
switch (platform.kind) {
DarwinPlatform.Kind.MacOS => try ctx.args.append(c"-macosx_version_min"),
DarwinPlatform.Kind.IPhoneOS => try ctx.args.append(c"-iphoneos_version_min"),
DarwinPlatform.Kind.IPhoneOSSimulator => try ctx.args.append(c"-ios_simulator_version_min"),
}
const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro);
try ctx.args.append(ver_str.ptr);
if (ctx.comp.kind == Compilation.Kind.Exe) {
if (ctx.comp.is_static) {
try ctx.args.append(c"-no_pie");
} else {
try ctx.args.append(c"-pie");
}
}
try ctx.args.append(c"-o");
try ctx.args.append(ctx.out_file_path.ptr());
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
// Buf *rpath = g->rpath_list.at(i);
// add_rpath(lj, rpath);
//}
//add_rpath(lj, &lj->out_file);
if (shared) {
try ctx.args.append(c"-headerpad_max_install_names");
} else if (ctx.comp.is_static) {
try ctx.args.append(c"-lcrt0.o");
} else {
switch (platform.kind) {
DarwinPlatform.Kind.MacOS => {
if (platform.versionLessThan(10, 5)) {
try ctx.args.append(c"-lcrt1.o");
} else if (platform.versionLessThan(10, 6)) {
try ctx.args.append(c"-lcrt1.10.5.o");
} else if (platform.versionLessThan(10, 8)) {
try ctx.args.append(c"-lcrt1.10.6.o");
}
},
DarwinPlatform.Kind.IPhoneOS => {
if (ctx.comp.target.getArch() == builtin.Arch.aarch64) {
// iOS does not need any crt1 files for arm64
} else if (platform.versionLessThan(3, 1)) {
try ctx.args.append(c"-lcrt1.o");
} else if (platform.versionLessThan(6, 0)) {
try ctx.args.append(c"-lcrt1.3.1.o");
}
},
DarwinPlatform.Kind.IPhoneOSSimulator => {}, // no crt1.o needed
}
}
//for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
// const char *lib_dir = g->lib_dirs.at(i);
// lj->args.append("-L");
// lj->args.append(lib_dir);
//}
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
try ctx.args.append(link_obj_with_null.ptr);
}
try addFnObjects(ctx);
//// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
//if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
// Buf *compiler_rt_o_path = build_compiler_rt(g);
// lj->args.append(buf_ptr(compiler_rt_o_path));
//}
if (ctx.comp.target == Target.Native) {
for (ctx.comp.link_libs_list.toSliceConst()) |lib| {
if (mem.eql(u8, lib.name, "c")) {
// on Darwin, libSystem has libc in it, but also you have to use it
// to make syscalls because the syscall numbers are not documented
// and change between versions.
// so we always link against libSystem
try ctx.args.append(c"-lSystem");
} else {
if (mem.indexOfScalar(u8, lib.name, '/') == null) {
const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name);
try ctx.args.append(arg.ptr);
} else {
const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
try ctx.args.append(arg.ptr);
}
}
}
} else {
try ctx.args.append(c"-undefined");
try ctx.args.append(c"dynamic_lookup");
}
if (platform.kind == DarwinPlatform.Kind.MacOS) {
if (platform.versionLessThan(10, 5)) {
try ctx.args.append(c"-lgcc_s.10.4");
} else if (platform.versionLessThan(10, 6)) {
try ctx.args.append(c"-lgcc_s.10.5");
}
} else {
@panic("TODO");
}
//for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) {
// lj->args.append("-framework");
// lj->args.append(buf_ptr(g->darwin_frameworks.at(i)));
//}
}
fn constructLinkerArgsWasm(ctx: *Context) void {
@panic("TODO");
}
fn addFnObjects(ctx: *Context) !void {
// at this point it's guaranteed nobody else has this lock, so we circumvent it
// and avoid having to be a coroutine
const fn_link_set = &ctx.comp.fn_link_set.private_data;
var it = fn_link_set.first;
while (it) |node| {
const fn_val = node.data orelse {
// handle the tombstone. See Value.Fn.destroy.
it = node.next;
fn_link_set.remove(node);
ctx.comp.gpa().destroy(node);
continue;
};
try ctx.args.append(fn_val.containing_object.ptr());
it = node.next;
}
}
const DarwinPlatform = struct {
kind: Kind,
major: u32,
minor: u32,
micro: u32,
const Kind = enum {
MacOS,
IPhoneOS,
IPhoneOSSimulator,
};
fn get(comp: *Compilation) !DarwinPlatform {
var result: DarwinPlatform = undefined;
const ver_str = switch (comp.darwin_version_min) {
Compilation.DarwinVersionMin.MacOS => |ver| blk: {
result.kind = Kind.MacOS;
break :blk ver;
},
Compilation.DarwinVersionMin.Ios => |ver| blk: {
result.kind = Kind.IPhoneOS;
break :blk ver;
},
Compilation.DarwinVersionMin.None => blk: {
assert(comp.target.getOs() == builtin.Os.macosx);
result.kind = Kind.MacOS;
break :blk "10.10";
},
};
var had_extra: bool = undefined;
try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,);
if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) {
return error.InvalidDarwinVersionString;
}
if (result.kind == Kind.IPhoneOS) {
switch (comp.target.getArch()) {
builtin.Arch.i386,
builtin.Arch.x86_64,
=> result.kind = Kind.IPhoneOSSimulator,
else => {},
}
}
return result;
}
fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool {
if (self.major < major)
return true;
if (self.major > major)
return false;
if (self.minor < minor)
return true;
return false;
}
};
/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the
/// grouped values as integers. Numbers which are not provided are set to 0.
/// return true if the entire string was parsed (9.2), or all groups were
/// parsed (10.3.5extrastuff).
fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void {
major.* = 0;
minor.* = 0;
micro.* = 0;
had_extra.* = false;
if (str.len == 0)
return error.InvalidDarwinVersionString;
var start_pos: usize = 0;
for ([]*u32{major, minor, micro}) |v| {
const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.');
const end_pos = dot_pos orelse str.len;
v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString;
start_pos = (dot_pos orelse return) + 1;
if (start_pos == str.len) return;
}
had_extra.* = true;
}

View file

@ -2,6 +2,12 @@ const builtin = @import("builtin");
const c = @import("c.zig"); const c = @import("c.zig");
const assert = @import("std").debug.assert; const assert = @import("std").debug.assert;
// we wrap the c module for 3 reasons:
// 1. to avoid accidentally calling the non-thread-safe functions
// 2. patch up some of the types to remove nullability
// 3. some functions have been augmented by zig_llvm.cpp to be more powerful,
// such as ZigLLVMTargetMachineEmitToFile
pub const AttributeIndex = c_uint; pub const AttributeIndex = c_uint;
pub const Bool = c_int; pub const Bool = c_int;
@ -12,25 +18,59 @@ pub const ValueRef = removeNullability(c.LLVMValueRef);
pub const TypeRef = removeNullability(c.LLVMTypeRef); pub const TypeRef = removeNullability(c.LLVMTypeRef);
pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef); pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef);
pub const AttributeRef = removeNullability(c.LLVMAttributeRef); pub const AttributeRef = removeNullability(c.LLVMAttributeRef);
pub const TargetRef = removeNullability(c.LLVMTargetRef);
pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef);
pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef);
pub const DIBuilder = c.ZigLLVMDIBuilder;
pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType;
pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex; pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;
pub const AddFunction = c.LLVMAddFunction; pub const AddFunction = c.LLVMAddFunction;
pub const AddGlobal = c.LLVMAddGlobal;
pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
pub const ArrayType = c.LLVMArrayType;
pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
pub const ConstAllOnes = c.LLVMConstAllOnes;
pub const ConstArray = c.LLVMConstArray;
pub const ConstBitCast = c.LLVMConstBitCast;
pub const ConstInt = c.LLVMConstInt; pub const ConstInt = c.LLVMConstInt;
pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision;
pub const ConstNeg = c.LLVMConstNeg;
pub const ConstNull = c.LLVMConstNull;
pub const ConstStringInContext = c.LLVMConstStringInContext; pub const ConstStringInContext = c.LLVMConstStringInContext;
pub const ConstStructInContext = c.LLVMConstStructInContext; pub const ConstStructInContext = c.LLVMConstStructInContext;
pub const CopyStringRepOfTargetData = c.LLVMCopyStringRepOfTargetData;
pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext; pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext;
pub const CreateCompileUnit = c.ZigLLVMCreateCompileUnit;
pub const CreateDIBuilder = c.ZigLLVMCreateDIBuilder;
pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute; pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute;
pub const CreateFile = c.ZigLLVMCreateFile;
pub const CreateStringAttribute = c.LLVMCreateStringAttribute; pub const CreateStringAttribute = c.LLVMCreateStringAttribute;
pub const CreateTargetDataLayout = c.LLVMCreateTargetDataLayout;
pub const CreateTargetMachine = c.LLVMCreateTargetMachine;
pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize;
pub const DisposeBuilder = c.LLVMDisposeBuilder; pub const DisposeBuilder = c.LLVMDisposeBuilder;
pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder;
pub const DisposeMessage = c.LLVMDisposeMessage;
pub const DisposeModule = c.LLVMDisposeModule; pub const DisposeModule = c.LLVMDisposeModule;
pub const DisposeTargetData = c.LLVMDisposeTargetData;
pub const DisposeTargetMachine = c.LLVMDisposeTargetMachine;
pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext; pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext;
pub const DumpModule = c.LLVMDumpModule; pub const DumpModule = c.LLVMDumpModule;
pub const FP128TypeInContext = c.LLVMFP128TypeInContext; pub const FP128TypeInContext = c.LLVMFP128TypeInContext;
pub const FloatTypeInContext = c.LLVMFloatTypeInContext; pub const FloatTypeInContext = c.LLVMFloatTypeInContext;
pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName; pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;
pub const GetHostCPUName = c.ZigLLVMGetHostCPUName;
pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext; pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;
pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures;
pub const GetUndef = c.LLVMGetUndef;
pub const HalfTypeInContext = c.LLVMHalfTypeInContext; pub const HalfTypeInContext = c.LLVMHalfTypeInContext;
pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers;
pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters;
pub const InitializeAllTargetInfos = c.LLVMInitializeAllTargetInfos;
pub const InitializeAllTargetMCs = c.LLVMInitializeAllTargetMCs;
pub const InitializeAllTargets = c.LLVMInitializeAllTargets;
pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext; pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext;
pub const Int128TypeInContext = c.LLVMInt128TypeInContext; pub const Int128TypeInContext = c.LLVMInt128TypeInContext;
pub const Int16TypeInContext = c.LLVMInt16TypeInContext; pub const Int16TypeInContext = c.LLVMInt16TypeInContext;
@ -47,13 +87,26 @@ pub const MDStringInContext = c.LLVMMDStringInContext;
pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext; pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;
pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext; pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;
pub const PointerType = c.LLVMPointerType;
pub const SetAlignment = c.LLVMSetAlignment;
pub const SetDataLayout = c.LLVMSetDataLayout;
pub const SetGlobalConstant = c.LLVMSetGlobalConstant;
pub const SetInitializer = c.LLVMSetInitializer;
pub const SetLinkage = c.LLVMSetLinkage;
pub const SetTarget = c.LLVMSetTarget;
pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
pub const StructTypeInContext = c.LLVMStructTypeInContext; pub const StructTypeInContext = c.LLVMStructTypeInContext;
pub const TokenTypeInContext = c.LLVMTokenTypeInContext; pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
pub const TypeOf = c.LLVMTypeOf;
pub const VoidTypeInContext = c.LLVMVoidTypeInContext; pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
pub const ConstAllOnes = c.LLVMConstAllOnes;
pub const ConstNull = c.LLVMConstNull; pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;
pub const GetTargetFromTriple = LLVMGetTargetFromTriple;
extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool;
pub const VerifyModule = LLVMVerifyModule; pub const VerifyModule = LLVMVerifyModule;
extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool; extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool;
@ -83,10 +136,66 @@ pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction;
pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction; pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction;
pub const VerifierFailureAction = c.LLVMVerifierFailureAction; pub const VerifierFailureAction = c.LLVMVerifierFailureAction;
pub const CodeGenLevelNone = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelNone;
pub const CodeGenLevelLess = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelLess;
pub const CodeGenLevelDefault = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault;
pub const CodeGenLevelAggressive = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive;
pub const CodeGenOptLevel = c.LLVMCodeGenOptLevel;
pub const RelocDefault = c.LLVMRelocMode.LLVMRelocDefault;
pub const RelocStatic = c.LLVMRelocMode.LLVMRelocStatic;
pub const RelocPIC = c.LLVMRelocMode.LLVMRelocPIC;
pub const RelocDynamicNoPic = c.LLVMRelocMode.LLVMRelocDynamicNoPic;
pub const RelocMode = c.LLVMRelocMode;
pub const CodeModelDefault = c.LLVMCodeModel.LLVMCodeModelDefault;
pub const CodeModelJITDefault = c.LLVMCodeModel.LLVMCodeModelJITDefault;
pub const CodeModelSmall = c.LLVMCodeModel.LLVMCodeModelSmall;
pub const CodeModelKernel = c.LLVMCodeModel.LLVMCodeModelKernel;
pub const CodeModelMedium = c.LLVMCodeModel.LLVMCodeModelMedium;
pub const CodeModelLarge = c.LLVMCodeModel.LLVMCodeModelLarge;
pub const CodeModel = c.LLVMCodeModel;
pub const EmitAssembly = EmitOutputType.ZigLLVM_EmitAssembly;
pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary;
pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr;
pub const EmitOutputType = c.ZigLLVM_EmitOutputType;
pub const CCallConv = c.LLVMCCallConv;
pub const FastCallConv = c.LLVMFastCallConv;
pub const ColdCallConv = c.LLVMColdCallConv;
pub const WebKitJSCallConv = c.LLVMWebKitJSCallConv;
pub const AnyRegCallConv = c.LLVMAnyRegCallConv;
pub const X86StdcallCallConv = c.LLVMX86StdcallCallConv;
pub const X86FastcallCallConv = c.LLVMX86FastcallCallConv;
pub const CallConv = c.LLVMCallConv;
pub const FnInline = extern enum {
Auto,
Always,
Never,
};
fn removeNullability(comptime T: type) type { fn removeNullability(comptime T: type) type {
comptime assert(@typeId(T) == builtin.TypeId.Optional); comptime assert(@typeId(T) == builtin.TypeId.Optional);
return T.Child; return T.Child;
} }
pub const BuildRet = LLVMBuildRet; pub const BuildRet = LLVMBuildRet;
extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef; extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef;
pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile;
extern fn ZigLLVMTargetMachineEmitToFile(
targ_machine_ref: TargetMachineRef,
module_ref: ModuleRef,
filename: [*]const u8,
output_type: EmitOutputType,
error_message: *[*]u8,
is_debug: bool,
is_small: bool,
) bool;
pub const BuildCall = ZigLLVMBuildCall;
extern fn ZigLLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: [*]ValueRef, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?ValueRef;
pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage;

View file

@ -18,6 +18,7 @@ const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
const Compilation = @import("compilation.zig").Compilation; const Compilation = @import("compilation.zig").Compilation;
const Target = @import("target.zig").Target; const Target = @import("target.zig").Target;
const errmsg = @import("errmsg.zig"); const errmsg = @import("errmsg.zig");
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
var stderr_file: os.File = undefined; var stderr_file: os.File = undefined;
var stderr: *io.OutStream(io.FileOutStream.Error) = undefined; var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
@ -28,13 +29,14 @@ const usage =
\\ \\
\\Commands: \\Commands:
\\ \\
\\ build-exe [source] Create executable from source or object files \\ build-exe [source] Create executable from source or object files
\\ build-lib [source] Create library from source or object files \\ build-lib [source] Create library from source or object files
\\ build-obj [source] Create object from source or assembly \\ build-obj [source] Create object from source or assembly
\\ fmt [source] Parse file and render in canonical zig format \\ fmt [source] Parse file and render in canonical zig format
\\ targets List available compilation targets \\ libc [paths_file] Display native libc paths file or validate one
\\ version Print version number and exit \\ targets List available compilation targets
\\ zen Print zen of zig and exit \\ version Print version number and exit
\\ zen Print zen of zig and exit
\\ \\
\\ \\
; ;
@ -85,6 +87,10 @@ pub fn main() !void {
.name = "fmt", .name = "fmt",
.exec = cmdFmt, .exec = cmdFmt,
}, },
Command{
.name = "libc",
.exec = cmdLibC,
},
Command{ Command{
.name = "targets", .name = "targets",
.exec = cmdTargets, .exec = cmdTargets,
@ -130,11 +136,10 @@ const usage_build_generic =
\\ --color [auto|off|on] Enable or disable colored error messages \\ --color [auto|off|on] Enable or disable colored error messages
\\ \\
\\Compile Options: \\Compile Options:
\\ --libc [file] Provide a file which specifies libc paths
\\ --assembly [source] Add assembly file to build \\ --assembly [source] Add assembly file to build
\\ --cache-dir [path] Override the cache directory
\\ --emit [filetype] Emit a specific file format as compilation output \\ --emit [filetype] Emit a specific file format as compilation output
\\ --enable-timing-info Print timing diagnostics \\ --enable-timing-info Print timing diagnostics
\\ --libc-include-dir [path] Directory where libc stdlib.h resides
\\ --name [name] Override output name \\ --name [name] Override output name
\\ --output [file] Override destination path \\ --output [file] Override destination path
\\ --output-h [file] Override generated header file path \\ --output-h [file] Override generated header file path
@ -163,12 +168,7 @@ const usage_build_generic =
\\ \\
\\Link Options: \\Link Options:
\\ --ar-path [path] Set the path to ar \\ --ar-path [path] Set the path to ar
\\ --dynamic-linker [path] Set the path to ld.so
\\ --each-lib-rpath Add rpath for each used dynamic library \\ --each-lib-rpath Add rpath for each used dynamic library
\\ --libc-lib-dir [path] Directory where libc crt1.o resides
\\ --libc-static-lib-dir [path] Directory where libc crtbegin.o resides
\\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
\\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
\\ --library [lib] Link against lib \\ --library [lib] Link against lib
\\ --forbid-library [lib] Make it an error to link against lib \\ --forbid-library [lib] Make it an error to link against lib
\\ --library-path [dir] Add a directory to the library search path \\ --library-path [dir] Add a directory to the library search path
@ -203,14 +203,13 @@ const args_build_generic = []Flag{
}), }),
Flag.ArgMergeN("--assembly", 1), Flag.ArgMergeN("--assembly", 1),
Flag.Arg1("--cache-dir"),
Flag.Option("--emit", []const []const u8{ Flag.Option("--emit", []const []const u8{
"asm", "asm",
"bin", "bin",
"llvm-ir", "llvm-ir",
}), }),
Flag.Bool("--enable-timing-info"), Flag.Bool("--enable-timing-info"),
Flag.Arg1("--libc-include-dir"), Flag.Arg1("--libc"),
Flag.Arg1("--name"), Flag.Arg1("--name"),
Flag.Arg1("--output"), Flag.Arg1("--output"),
Flag.Arg1("--output-h"), Flag.Arg1("--output-h"),
@ -234,12 +233,7 @@ const args_build_generic = []Flag{
Flag.Arg1("-mllvm"), Flag.Arg1("-mllvm"),
Flag.Arg1("--ar-path"), Flag.Arg1("--ar-path"),
Flag.Arg1("--dynamic-linker"),
Flag.Bool("--each-lib-rpath"), Flag.Bool("--each-lib-rpath"),
Flag.Arg1("--libc-lib-dir"),
Flag.Arg1("--libc-static-lib-dir"),
Flag.Arg1("--msvc-lib-dir"),
Flag.Arg1("--kernel32-lib-dir"),
Flag.ArgMergeN("--library", 1), Flag.ArgMergeN("--library", 1),
Flag.ArgMergeN("--forbid-library", 1), Flag.ArgMergeN("--forbid-library", 1),
Flag.ArgMergeN("--library-path", 1), Flag.ArgMergeN("--library-path", 1),
@ -363,6 +357,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
} }
}; };
const is_static = flags.present("static");
const assembly_files = flags.many("assembly"); const assembly_files = flags.many("assembly");
const link_objects = flags.many("object"); const link_objects = flags.many("object");
if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) { if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) {
@ -375,21 +371,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
os.exit(1); os.exit(1);
} }
const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..];
const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch {
try stderr.print("invalid cache dir: {}\n", rel_cache_dir);
os.exit(1);
};
defer allocator.free(full_cache_dir);
const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1); const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
defer allocator.free(zig_lib_dir); defer allocator.free(zig_lib_dir);
var override_libc: LibCInstallation = undefined;
var loop: event.Loop = undefined; var loop: event.Loop = undefined;
try loop.initMultiThreaded(allocator); try loop.initMultiThreaded(allocator);
defer loop.deinit(); defer loop.deinit();
var event_loop_local = EventLoopLocal.init(&loop); var event_loop_local = try EventLoopLocal.init(&loop);
defer event_loop_local.deinit(); defer event_loop_local.deinit();
var comp = try Compilation.create( var comp = try Compilation.create(
@ -399,11 +390,20 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
Target.Native, Target.Native,
out_type, out_type,
build_mode, build_mode,
is_static,
zig_lib_dir, zig_lib_dir,
full_cache_dir,
); );
defer comp.destroy(); defer comp.destroy();
if (flags.single("libc")) |libc_path| {
parseLibcPaths(loop.allocator, &override_libc, libc_path);
comp.override_libc = &override_libc;
}
for (flags.many("library")) |lib| {
_ = try comp.addLinkLib(lib, true);
}
comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10); comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10); comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10); comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
@ -426,26 +426,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
comp.clang_argv = clang_argv_buf.toSliceConst(); comp.clang_argv = clang_argv_buf.toSliceConst();
comp.strip = flags.present("strip"); comp.strip = flags.present("strip");
comp.is_static = flags.present("static");
if (flags.single("libc-lib-dir")) |libc_lib_dir| {
comp.libc_lib_dir = libc_lib_dir;
}
if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
comp.libc_static_lib_dir = libc_static_lib_dir;
}
if (flags.single("libc-include-dir")) |libc_include_dir| {
comp.libc_include_dir = libc_include_dir;
}
if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {
comp.msvc_lib_dir = msvc_lib_dir;
}
if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {
comp.kernel32_lib_dir = kernel32_lib_dir;
}
if (flags.single("dynamic-linker")) |dynamic_linker| {
comp.dynamic_linker = dynamic_linker;
}
comp.verbose_tokenize = flags.present("verbose-tokenize"); comp.verbose_tokenize = flags.present("verbose-tokenize");
comp.verbose_ast_tree = flags.present("verbose-ast-tree"); comp.verbose_ast_tree = flags.present("verbose-ast-tree");
@ -481,9 +461,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
} }
comp.emit_file_type = emit_type; comp.emit_file_type = emit_type;
comp.link_objects = link_objects;
comp.assembly_files = assembly_files; comp.assembly_files = assembly_files;
comp.link_out_file = flags.single("out-file"); comp.link_out_file = flags.single("output");
comp.link_objects = link_objects;
try comp.build(); try comp.build();
const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color); const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);
@ -497,7 +477,6 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
switch (build_event) { switch (build_event) {
Compilation.Event.Ok => { Compilation.Event.Ok => {
std.debug.warn("Build succeeded\n");
return; return;
}, },
Compilation.Event.Error => |err| { Compilation.Event.Error => |err| {
@ -506,7 +485,8 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
}, },
Compilation.Event.Fail => |msgs| { Compilation.Event.Fail => |msgs| {
for (msgs) |msg| { for (msgs) |msg| {
errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1); defer msg.destroy();
msg.printToFile(&stderr_file, color) catch os.exit(1);
} }
}, },
} }
@ -577,6 +557,53 @@ const Fmt = struct {
} }
}; };
fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
libc.parse(allocator, libc_paths_file, stderr) catch |err| {
stderr.print(
"Unable to parse libc path file '{}': {}.\n" ++
"Try running `zig libc` to see an example for the native target.\n",
libc_paths_file,
@errorName(err),
) catch os.exit(1);
os.exit(1);
};
}
fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
switch (args.len) {
0 => {},
1 => {
var libc_installation: LibCInstallation = undefined;
parseLibcPaths(allocator, &libc_installation, args[0]);
return;
},
else => {
try stderr.print("unexpected extra parameter: {}\n", args[1]);
os.exit(1);
},
}
var loop: event.Loop = undefined;
try loop.initMultiThreaded(allocator);
defer loop.deinit();
var event_loop_local = try EventLoopLocal.init(&loop);
defer event_loop_local.deinit();
const handle = try async<loop.allocator> findLibCAsync(&event_loop_local);
defer cancel handle;
loop.run();
}
async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {
const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| {
stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
os.exit(1);
};
libc.render(stdout) catch os.exit(1);
}
fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
var flags = try Args.parse(allocator, args_fmt_spec, args); var flags = try Args.parse(allocator, args_fmt_spec, args);
defer flags.deinit(); defer flags.deinit();
@ -620,10 +647,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
var error_it = tree.errors.iterator(0); var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| { while (error_it.next()) |parse_error| {
const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, "<stdin>"); const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
defer allocator.destroy(msg); defer msg.destroy();
try errmsg.printToFile(&stderr_file, msg, color); try msg.printToFile(&stderr_file, color);
} }
if (tree.errors.len != 0) { if (tree.errors.len != 0) {
os.exit(1); os.exit(1);
@ -676,10 +703,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
var error_it = tree.errors.iterator(0); var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| { while (error_it.next()) |parse_error| {
const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path); const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path);
defer allocator.destroy(msg); defer msg.destroy();
try errmsg.printToFile(&stderr_file, msg, color); try msg.printToFile(&stderr_file, color);
} }
if (tree.errors.len != 0) { if (tree.errors.len != 0) {
fmt.any_error = true; fmt.any_error = true;

View file

@ -0,0 +1,29 @@
const std = @import("std");
const mem = std.mem;
const assert = std.debug.assert;
const Buffer = std.Buffer;
pub const Package = struct {
root_src_dir: Buffer,
root_src_path: Buffer,
/// relative to root_src_dir
table: Table,
pub const Table = std.HashMap([]const u8, *Package, mem.hash_slice_u8, mem.eql_slice_u8);
/// makes internal copies of root_src_dir and root_src_path
/// allocator should be an arena allocator because Package never frees anything
pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package {
return allocator.create(Package{
.root_src_dir = try Buffer.init(allocator, root_src_dir),
.root_src_path = try Buffer.init(allocator, root_src_path),
.table = Table.init(allocator),
});
}
pub fn add(self: *Package, name: []const u8, package: *Package) !void {
const entry = try self.table.put(try mem.dupe(self.table.allocator, u8, name), package);
assert(entry == null);
}
};

View file

@ -1,6 +0,0 @@
const ast = @import("std").zig.ast;
pub const ParsedFile = struct {
tree: ast.Tree,
realpath: []const u8,
};

View file

@ -8,6 +8,8 @@ const ast = std.zig.ast;
const Value = @import("value.zig").Value; const Value = @import("value.zig").Value;
const ir = @import("ir.zig"); const ir = @import("ir.zig");
const Span = @import("errmsg.zig").Span; const Span = @import("errmsg.zig").Span;
const assert = std.debug.assert;
const event = std.event;
pub const Scope = struct { pub const Scope = struct {
id: Id, id: Id,
@ -23,7 +25,8 @@ pub const Scope = struct {
if (base.ref_count == 0) { if (base.ref_count == 0) {
if (base.parent) |parent| parent.deref(comp); if (base.parent) |parent| parent.deref(comp);
switch (base.id) { switch (base.id) {
Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(), Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp),
Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(comp),
Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp), Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp),
Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp), Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp),
Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp), Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
@ -33,6 +36,15 @@ pub const Scope = struct {
} }
} }
pub fn findRoot(base: *Scope) *Root {
var scope = base;
while (scope.parent) |parent| {
scope = parent;
}
assert(scope.id == Id.Root);
return @fieldParentPtr(Root, "base", scope);
}
pub fn findFnDef(base: *Scope) ?*FnDef { pub fn findFnDef(base: *Scope) ?*FnDef {
var scope = base; var scope = base;
while (true) { while (true) {
@ -44,12 +56,33 @@ pub const Scope = struct {
Id.Defer, Id.Defer,
Id.DeferExpr, Id.DeferExpr,
Id.CompTime, Id.CompTime,
Id.Root,
=> scope = scope.parent orelse return null,
}
}
}
pub fn findDeferExpr(base: *Scope) ?*DeferExpr {
var scope = base;
while (true) {
switch (scope.id) {
Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base),
Id.FnDef,
Id.Decls,
=> return null,
Id.Block,
Id.Defer,
Id.CompTime,
Id.Root,
=> scope = scope.parent orelse return null, => scope = scope.parent orelse return null,
} }
} }
} }
pub const Id = enum { pub const Id = enum {
Root,
Decls, Decls,
Block, Block,
FnDef, FnDef,
@ -58,42 +91,82 @@ pub const Scope = struct {
DeferExpr, DeferExpr,
}; };
pub const Root = struct {
base: Scope,
tree: *ast.Tree,
realpath: []const u8,
/// Creates a Root scope with 1 reference
/// Takes ownership of realpath
/// Takes ownership of tree, will deinit and destroy when done.
pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
const self = try comp.gpa().create(Root{
.base = Scope{
.id = Id.Root,
.parent = null,
.ref_count = 1,
},
.tree = tree,
.realpath = realpath,
});
errdefer comp.gpa().destroy(self);
return self;
}
pub fn destroy(self: *Root, comp: *Compilation) void {
comp.gpa().free(self.tree.source);
self.tree.deinit();
comp.gpa().destroy(self.tree);
comp.gpa().free(self.realpath);
comp.gpa().destroy(self);
}
};
pub const Decls = struct { pub const Decls = struct {
base: Scope, base: Scope,
table: Decl.Table,
/// The lock must be respected for writing. However once name_future resolves,
/// readers can freely access it.
table: event.Locked(Decl.Table),
/// Once this future is resolved, the table is complete and available for unlocked
/// read-only access. It does not mean all the decls are resolved; it means only that
/// the table has all the names. Each decl in the table has its own resolution state.
name_future: event.Future(void),
/// Creates a Decls scope with 1 reference /// Creates a Decls scope with 1 reference
pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls { pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
const self = try comp.a().create(Decls{ const self = try comp.gpa().create(Decls{
.base = Scope{ .base = Scope{
.id = Id.Decls, .id = Id.Decls,
.parent = parent, .parent = parent,
.ref_count = 1, .ref_count = 1,
}, },
.table = undefined, .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
.name_future = event.Future(void).init(comp.loop),
}); });
errdefer comp.a().destroy(self); parent.ref();
self.table = Decl.Table.init(comp.a());
errdefer self.table.deinit();
if (parent) |p| p.ref();
return self; return self;
} }
pub fn destroy(self: *Decls) void { pub fn destroy(self: *Decls, comp: *Compilation) void {
self.table.deinit(); self.table.deinit();
self.table.allocator.destroy(self); comp.gpa().destroy(self);
}
pub async fn getTableReadOnly(self: *Decls) *Decl.Table {
_ = await (async self.name_future.get() catch unreachable);
return &self.table.private_data;
} }
}; };
pub const Block = struct { pub const Block = struct {
base: Scope, base: Scope,
incoming_values: std.ArrayList(*ir.Instruction), incoming_values: std.ArrayList(*ir.Inst),
incoming_blocks: std.ArrayList(*ir.BasicBlock), incoming_blocks: std.ArrayList(*ir.BasicBlock),
end_block: *ir.BasicBlock, end_block: *ir.BasicBlock,
is_comptime: *ir.Instruction, is_comptime: *ir.Inst,
safety: Safety, safety: Safety,
@ -125,8 +198,8 @@ pub const Scope = struct {
}; };
/// Creates a Block scope with 1 reference /// Creates a Block scope with 1 reference
pub fn create(comp: *Compilation, parent: ?*Scope) !*Block { pub fn create(comp: *Compilation, parent: *Scope) !*Block {
const self = try comp.a().create(Block{ const self = try comp.gpa().create(Block{
.base = Scope{ .base = Scope{
.id = Id.Block, .id = Id.Block,
.parent = parent, .parent = parent,
@ -138,14 +211,14 @@ pub const Scope = struct {
.is_comptime = undefined, .is_comptime = undefined,
.safety = Safety.Auto, .safety = Safety.Auto,
}); });
errdefer comp.a().destroy(self); errdefer comp.gpa().destroy(self);
if (parent) |p| p.ref(); parent.ref();
return self; return self;
} }
pub fn destroy(self: *Block, comp: *Compilation) void { pub fn destroy(self: *Block, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -157,8 +230,8 @@ pub const Scope = struct {
/// Creates a FnDef scope with 1 reference /// Creates a FnDef scope with 1 reference
/// Must set the fn_val later /// Must set the fn_val later
pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef { pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {
const self = try comp.a().create(FnDef{ const self = try comp.gpa().create(FnDef{
.base = Scope{ .base = Scope{
.id = Id.FnDef, .id = Id.FnDef,
.parent = parent, .parent = parent,
@ -167,13 +240,13 @@ pub const Scope = struct {
.fn_val = undefined, .fn_val = undefined,
}); });
if (parent) |p| p.ref(); parent.ref();
return self; return self;
} }
pub fn destroy(self: *FnDef, comp: *Compilation) void { pub fn destroy(self: *FnDef, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -181,8 +254,8 @@ pub const Scope = struct {
base: Scope, base: Scope,
/// Creates a CompTime scope with 1 reference /// Creates a CompTime scope with 1 reference
pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime { pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {
const self = try comp.a().create(CompTime{ const self = try comp.gpa().create(CompTime{
.base = Scope{ .base = Scope{
.id = Id.CompTime, .id = Id.CompTime,
.parent = parent, .parent = parent,
@ -190,12 +263,12 @@ pub const Scope = struct {
}, },
}); });
if (parent) |p| p.ref(); parent.ref();
return self; return self;
} }
pub fn destroy(self: *CompTime, comp: *Compilation) void { pub fn destroy(self: *CompTime, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -212,11 +285,11 @@ pub const Scope = struct {
/// Creates a Defer scope with 1 reference /// Creates a Defer scope with 1 reference
pub fn create( pub fn create(
comp: *Compilation, comp: *Compilation,
parent: ?*Scope, parent: *Scope,
kind: Kind, kind: Kind,
defer_expr_scope: *DeferExpr, defer_expr_scope: *DeferExpr,
) !*Defer { ) !*Defer {
const self = try comp.a().create(Defer{ const self = try comp.gpa().create(Defer{
.base = Scope{ .base = Scope{
.id = Id.Defer, .id = Id.Defer,
.parent = parent, .parent = parent,
@ -225,42 +298,44 @@ pub const Scope = struct {
.defer_expr_scope = defer_expr_scope, .defer_expr_scope = defer_expr_scope,
.kind = kind, .kind = kind,
}); });
errdefer comp.a().destroy(self); errdefer comp.gpa().destroy(self);
defer_expr_scope.base.ref(); defer_expr_scope.base.ref();
if (parent) |p| p.ref(); parent.ref();
return self; return self;
} }
pub fn destroy(self: *Defer, comp: *Compilation) void { pub fn destroy(self: *Defer, comp: *Compilation) void {
self.defer_expr_scope.base.deref(comp); self.defer_expr_scope.base.deref(comp);
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
pub const DeferExpr = struct { pub const DeferExpr = struct {
base: Scope, base: Scope,
expr_node: *ast.Node, expr_node: *ast.Node,
reported_err: bool,
/// Creates a DeferExpr scope with 1 reference /// Creates a DeferExpr scope with 1 reference
pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr { pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {
const self = try comp.a().create(DeferExpr{ const self = try comp.gpa().create(DeferExpr{
.base = Scope{ .base = Scope{
.id = Id.DeferExpr, .id = Id.DeferExpr,
.parent = parent, .parent = parent,
.ref_count = 1, .ref_count = 1,
}, },
.expr_node = expr_node, .expr_node = expr_node,
.reported_err = false,
}); });
errdefer comp.a().destroy(self); errdefer comp.gpa().destroy(self);
if (parent) |p| p.ref(); parent.ref();
return self; return self;
} }
pub fn destroy(self: *DeferExpr, comp: *Compilation) void { pub fn destroy(self: *DeferExpr, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
}; };

View file

@ -1,60 +1,562 @@
const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const c = @import("c.zig"); const llvm = @import("llvm.zig");
const CInt = @import("c_int.zig").CInt;
pub const CrossTarget = struct { pub const FloatAbi = enum {
arch: builtin.Arch, Hard,
os: builtin.Os, Soft,
environ: builtin.Environ, SoftFp,
}; };
pub const Target = union(enum) { pub const Target = union(enum) {
Native, Native,
Cross: CrossTarget, Cross: Cross,
pub fn oFileExt(self: *const Target) []const u8 { pub const Cross = struct {
const environ = switch (self.*) { arch: builtin.Arch,
Target.Native => builtin.environ, os: builtin.Os,
Target.Cross => |t| t.environ, environ: builtin.Environ,
}; object_format: builtin.ObjectFormat,
return switch (environ) { };
builtin.Environ.msvc => ".obj",
pub fn objFileExt(self: Target) []const u8 {
return switch (self.getObjectFormat()) {
builtin.ObjectFormat.coff => ".obj",
else => ".o", else => ".o",
}; };
} }
pub fn exeFileExt(self: *const Target) []const u8 { pub fn exeFileExt(self: Target) []const u8 {
return switch (self.getOs()) { return switch (self.getOs()) {
builtin.Os.windows => ".exe", builtin.Os.windows => ".exe",
else => "", else => "",
}; };
} }
pub fn getOs(self: *const Target) builtin.Os { pub fn libFileExt(self: Target, is_static: bool) []const u8 {
return switch (self.*) { return switch (self.getOs()) {
Target.Native => builtin.os, builtin.Os.windows => if (is_static) ".lib" else ".dll",
Target.Cross => |t| t.os, else => if (is_static) ".a" else ".so",
}; };
} }
pub fn isDarwin(self: *const Target) bool { pub fn getOs(self: Target) builtin.Os {
return switch (self) {
Target.Native => builtin.os,
@TagType(Target).Cross => |t| t.os,
};
}
pub fn getArch(self: Target) builtin.Arch {
return switch (self) {
Target.Native => builtin.arch,
@TagType(Target).Cross => |t| t.arch,
};
}
pub fn getEnviron(self: Target) builtin.Environ {
return switch (self) {
Target.Native => builtin.environ,
@TagType(Target).Cross => |t| t.environ,
};
}
pub fn getObjectFormat(self: Target) builtin.ObjectFormat {
return switch (self) {
Target.Native => builtin.object_format,
@TagType(Target).Cross => |t| t.object_format,
};
}
pub fn isWasm(self: Target) bool {
return switch (self.getArch()) {
builtin.Arch.wasm32, builtin.Arch.wasm64 => true,
else => false,
};
}
pub fn isDarwin(self: Target) bool {
return switch (self.getOs()) { return switch (self.getOs()) {
builtin.Os.ios, builtin.Os.macosx => true, builtin.Os.ios, builtin.Os.macosx => true,
else => false, else => false,
}; };
} }
pub fn isWindows(self: *const Target) bool { pub fn isWindows(self: Target) bool {
return switch (self.getOs()) { return switch (self.getOs()) {
builtin.Os.windows => true, builtin.Os.windows => true,
else => false, else => false,
}; };
} }
};
pub fn initializeAll() void { /// TODO expose the arch and subarch separately
c.LLVMInitializeAllTargets(); pub fn isArmOrThumb(self: Target) bool {
c.LLVMInitializeAllTargetInfos(); return switch (self.getArch()) {
c.LLVMInitializeAllTargetMCs(); builtin.Arch.armv8_3a,
c.LLVMInitializeAllAsmPrinters(); builtin.Arch.armv8_2a,
c.LLVMInitializeAllAsmParsers(); builtin.Arch.armv8_1a,
} builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
builtin.Arch.armebv8_3a,
builtin.Arch.armebv8_2a,
builtin.Arch.armebv8_1a,
builtin.Arch.armebv8,
builtin.Arch.armebv8r,
builtin.Arch.armebv8m_baseline,
builtin.Arch.armebv8m_mainline,
builtin.Arch.armebv7,
builtin.Arch.armebv7em,
builtin.Arch.armebv7m,
builtin.Arch.armebv7s,
builtin.Arch.armebv7k,
builtin.Arch.armebv7ve,
builtin.Arch.armebv6,
builtin.Arch.armebv6m,
builtin.Arch.armebv6k,
builtin.Arch.armebv6t2,
builtin.Arch.armebv5,
builtin.Arch.armebv5te,
builtin.Arch.armebv4t,
builtin.Arch.thumb,
builtin.Arch.thumbeb,
=> true,
else => false,
};
}
pub fn initializeAll() void {
llvm.InitializeAllTargets();
llvm.InitializeAllTargetInfos();
llvm.InitializeAllTargetMCs();
llvm.InitializeAllAsmPrinters();
llvm.InitializeAllAsmParsers();
}
pub fn getTriple(self: Target, allocator: *std.mem.Allocator) !std.Buffer {
var result = try std.Buffer.initSize(allocator, 0);
errdefer result.deinit();
// LLVM WebAssembly output support requires the target to be activated at
// build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly.
//
// LLVM determines the output format based on the environment suffix,
// defaulting to an object based on the architecture. The default format in
// LLVM 6 sets the wasm arch output incorrectly to ELF. We need to
// explicitly set this ourself in order for it to work.
//
// This is fixed in LLVM 7 and you will be able to get wasm output by
// using the target triple `wasm32-unknown-unknown-unknown`.
const env_name = if (self.isWasm()) "wasm" else @tagName(self.getEnviron());
var out = &std.io.BufferOutStream.init(&result).stream;
try out.print("{}-unknown-{}-{}", @tagName(self.getArch()), @tagName(self.getOs()), env_name);
return result;
}
pub fn is64bit(self: Target) bool {
return self.getArchPtrBitWidth() == 64;
}
pub fn getArchPtrBitWidth(self: Target) u32 {
switch (self.getArch()) {
builtin.Arch.avr,
builtin.Arch.msp430,
=> return 16,
builtin.Arch.arc,
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
builtin.Arch.armebv8_3a,
builtin.Arch.armebv8_2a,
builtin.Arch.armebv8_1a,
builtin.Arch.armebv8,
builtin.Arch.armebv8r,
builtin.Arch.armebv8m_baseline,
builtin.Arch.armebv8m_mainline,
builtin.Arch.armebv7,
builtin.Arch.armebv7em,
builtin.Arch.armebv7m,
builtin.Arch.armebv7s,
builtin.Arch.armebv7k,
builtin.Arch.armebv7ve,
builtin.Arch.armebv6,
builtin.Arch.armebv6m,
builtin.Arch.armebv6k,
builtin.Arch.armebv6t2,
builtin.Arch.armebv5,
builtin.Arch.armebv5te,
builtin.Arch.armebv4t,
builtin.Arch.hexagon,
builtin.Arch.le32,
builtin.Arch.mips,
builtin.Arch.mipsel,
builtin.Arch.nios2,
builtin.Arch.powerpc,
builtin.Arch.r600,
builtin.Arch.riscv32,
builtin.Arch.sparc,
builtin.Arch.sparcel,
builtin.Arch.tce,
builtin.Arch.tcele,
builtin.Arch.thumb,
builtin.Arch.thumbeb,
builtin.Arch.i386,
builtin.Arch.xcore,
builtin.Arch.nvptx,
builtin.Arch.amdil,
builtin.Arch.hsail,
builtin.Arch.spir,
builtin.Arch.kalimbav3,
builtin.Arch.kalimbav4,
builtin.Arch.kalimbav5,
builtin.Arch.shave,
builtin.Arch.lanai,
builtin.Arch.wasm32,
builtin.Arch.renderscript32,
=> return 32,
builtin.Arch.aarch64,
builtin.Arch.aarch64_be,
builtin.Arch.mips64,
builtin.Arch.mips64el,
builtin.Arch.powerpc64,
builtin.Arch.powerpc64le,
builtin.Arch.riscv64,
builtin.Arch.x86_64,
builtin.Arch.nvptx64,
builtin.Arch.le64,
builtin.Arch.amdil64,
builtin.Arch.hsail64,
builtin.Arch.spir64,
builtin.Arch.wasm64,
builtin.Arch.renderscript64,
builtin.Arch.amdgcn,
builtin.Arch.bpfel,
builtin.Arch.bpfeb,
builtin.Arch.sparcv9,
builtin.Arch.s390x,
=> return 64,
}
}
pub fn getFloatAbi(self: Target) FloatAbi {
return switch (self.getEnviron()) {
builtin.Environ.gnueabihf,
builtin.Environ.eabihf,
builtin.Environ.musleabihf,
=> FloatAbi.Hard,
else => FloatAbi.Soft,
};
}
pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
const env = self.getEnviron();
const arch = self.getArch();
switch (env) {
builtin.Environ.android => {
if (self.is64bit()) {
return "/system/bin/linker64";
} else {
return "/system/bin/linker";
}
},
builtin.Environ.gnux32 => {
if (arch == builtin.Arch.x86_64) {
return "/libx32/ld-linux-x32.so.2";
}
},
builtin.Environ.musl,
builtin.Environ.musleabi,
builtin.Environ.musleabihf,
=> {
if (arch == builtin.Arch.x86_64) {
return "/lib/ld-musl-x86_64.so.1";
}
},
else => {},
}
switch (arch) {
builtin.Arch.i386,
builtin.Arch.sparc,
builtin.Arch.sparcel,
=> return "/lib/ld-linux.so.2",
builtin.Arch.aarch64 => return "/lib/ld-linux-aarch64.so.1",
builtin.Arch.aarch64_be => return "/lib/ld-linux-aarch64_be.so.1",
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
builtin.Arch.thumb,
=> return switch (self.getFloatAbi()) {
FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
else => return "/lib/ld-linux.so.3",
},
builtin.Arch.armebv8_3a,
builtin.Arch.armebv8_2a,
builtin.Arch.armebv8_1a,
builtin.Arch.armebv8,
builtin.Arch.armebv8r,
builtin.Arch.armebv8m_baseline,
builtin.Arch.armebv8m_mainline,
builtin.Arch.armebv7,
builtin.Arch.armebv7em,
builtin.Arch.armebv7m,
builtin.Arch.armebv7s,
builtin.Arch.armebv7k,
builtin.Arch.armebv7ve,
builtin.Arch.armebv6,
builtin.Arch.armebv6m,
builtin.Arch.armebv6k,
builtin.Arch.armebv6t2,
builtin.Arch.armebv5,
builtin.Arch.armebv5te,
builtin.Arch.armebv4t,
builtin.Arch.thumbeb,
=> return switch (self.getFloatAbi()) {
FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
else => return "/lib/ld-linux.so.3",
},
builtin.Arch.mips,
builtin.Arch.mipsel,
builtin.Arch.mips64,
builtin.Arch.mips64el,
=> return null,
builtin.Arch.powerpc => return "/lib/ld.so.1",
builtin.Arch.powerpc64 => return "/lib64/ld64.so.2",
builtin.Arch.powerpc64le => return "/lib64/ld64.so.2",
builtin.Arch.s390x => return "/lib64/ld64.so.1",
builtin.Arch.sparcv9 => return "/lib64/ld-linux.so.2",
builtin.Arch.x86_64 => return "/lib64/ld-linux-x86-64.so.2",
builtin.Arch.arc,
builtin.Arch.avr,
builtin.Arch.bpfel,
builtin.Arch.bpfeb,
builtin.Arch.hexagon,
builtin.Arch.msp430,
builtin.Arch.nios2,
builtin.Arch.r600,
builtin.Arch.amdgcn,
builtin.Arch.riscv32,
builtin.Arch.riscv64,
builtin.Arch.tce,
builtin.Arch.tcele,
builtin.Arch.xcore,
builtin.Arch.nvptx,
builtin.Arch.nvptx64,
builtin.Arch.le32,
builtin.Arch.le64,
builtin.Arch.amdil,
builtin.Arch.amdil64,
builtin.Arch.hsail,
builtin.Arch.hsail64,
builtin.Arch.spir,
builtin.Arch.spir64,
builtin.Arch.kalimbav3,
builtin.Arch.kalimbav4,
builtin.Arch.kalimbav5,
builtin.Arch.shave,
builtin.Arch.lanai,
builtin.Arch.wasm32,
builtin.Arch.wasm64,
builtin.Arch.renderscript32,
builtin.Arch.renderscript64,
=> return null,
}
}
pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef {
var result: llvm.TargetRef = undefined;
var err_msg: [*]u8 = undefined;
if (llvm.GetTargetFromTriple(triple.ptr(), &result, &err_msg) != 0) {
std.debug.warn("triple: {s} error: {s}\n", triple.ptr(), err_msg);
return error.UnsupportedTarget;
}
return result;
}
pub fn cIntTypeSizeInBits(self: Target, id: CInt.Id) u32 {
const arch = self.getArch();
switch (self.getOs()) {
builtin.Os.freestanding => switch (self.getArch()) {
builtin.Arch.msp430 => switch (id) {
CInt.Id.Short,
CInt.Id.UShort,
CInt.Id.Int,
CInt.Id.UInt,
=> return 16,
CInt.Id.Long,
CInt.Id.ULong,
=> return 32,
CInt.Id.LongLong,
CInt.Id.ULongLong,
=> return 64,
},
else => switch (id) {
CInt.Id.Short,
CInt.Id.UShort,
=> return 16,
CInt.Id.Int,
CInt.Id.UInt,
=> return 32,
CInt.Id.Long,
CInt.Id.ULong,
=> return self.getArchPtrBitWidth(),
CInt.Id.LongLong,
CInt.Id.ULongLong,
=> return 64,
},
},
builtin.Os.linux,
builtin.Os.macosx,
builtin.Os.openbsd,
builtin.Os.zen,
=> switch (id) {
CInt.Id.Short,
CInt.Id.UShort,
=> return 16,
CInt.Id.Int,
CInt.Id.UInt,
=> return 32,
CInt.Id.Long,
CInt.Id.ULong,
=> return self.getArchPtrBitWidth(),
CInt.Id.LongLong,
CInt.Id.ULongLong,
=> return 64,
},
builtin.Os.windows => switch (id) {
CInt.Id.Short,
CInt.Id.UShort,
=> return 16,
CInt.Id.Int,
CInt.Id.UInt,
=> return 32,
CInt.Id.Long,
CInt.Id.ULong,
CInt.Id.LongLong,
CInt.Id.ULongLong,
=> return 64,
},
builtin.Os.ananas,
builtin.Os.cloudabi,
builtin.Os.dragonfly,
builtin.Os.freebsd,
builtin.Os.fuchsia,
builtin.Os.ios,
builtin.Os.kfreebsd,
builtin.Os.lv2,
builtin.Os.netbsd,
builtin.Os.solaris,
builtin.Os.haiku,
builtin.Os.minix,
builtin.Os.rtems,
builtin.Os.nacl,
builtin.Os.cnk,
builtin.Os.aix,
builtin.Os.cuda,
builtin.Os.nvcl,
builtin.Os.amdhsa,
builtin.Os.ps4,
builtin.Os.elfiamcu,
builtin.Os.tvos,
builtin.Os.watchos,
builtin.Os.mesa3d,
builtin.Os.contiki,
builtin.Os.amdpal,
=> @panic("TODO specify the C integer type sizes for this OS"),
}
}
pub fn getDarwinArchString(self: Target) []const u8 {
const arch = self.getArch();
switch (arch) {
builtin.Arch.aarch64 => return "arm64",
builtin.Arch.thumb,
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
=> return "arm",
builtin.Arch.powerpc => return "ppc",
builtin.Arch.powerpc64 => return "ppc64",
builtin.Arch.powerpc64le => return "ppc64le",
else => return @tagName(arch),
}
}
};

View file

@ -8,12 +8,14 @@ const assertOrPanic = std.debug.assertOrPanic;
const errmsg = @import("errmsg.zig"); const errmsg = @import("errmsg.zig");
const EventLoopLocal = @import("compilation.zig").EventLoopLocal; const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
test "compile errors" { var ctx: TestContext = undefined;
var ctx: TestContext = undefined;
test "stage2" {
try ctx.init(); try ctx.init();
defer ctx.deinit(); defer ctx.deinit();
try @import("../test/stage2/compile_errors.zig").addCases(&ctx); try @import("../test/stage2/compile_errors.zig").addCases(&ctx);
try @import("../test/stage2/compare_output.zig").addCases(&ctx);
try ctx.run(); try ctx.run();
} }
@ -25,7 +27,6 @@ pub const TestContext = struct {
loop: std.event.Loop, loop: std.event.Loop,
event_loop_local: EventLoopLocal, event_loop_local: EventLoopLocal,
zig_lib_dir: []u8, zig_lib_dir: []u8,
zig_cache_dir: []u8,
file_index: std.atomic.Int(usize), file_index: std.atomic.Int(usize),
group: std.event.Group(error!void), group: std.event.Group(error!void),
any_err: error!void, any_err: error!void,
@ -38,7 +39,6 @@ pub const TestContext = struct {
.loop = undefined, .loop = undefined,
.event_loop_local = undefined, .event_loop_local = undefined,
.zig_lib_dir = undefined, .zig_lib_dir = undefined,
.zig_cache_dir = undefined,
.group = undefined, .group = undefined,
.file_index = std.atomic.Int(usize).init(0), .file_index = std.atomic.Int(usize).init(0),
}; };
@ -46,7 +46,7 @@ pub const TestContext = struct {
try self.loop.initMultiThreaded(allocator); try self.loop.initMultiThreaded(allocator);
errdefer self.loop.deinit(); errdefer self.loop.deinit();
self.event_loop_local = EventLoopLocal.init(&self.loop); self.event_loop_local = try EventLoopLocal.init(&self.loop);
errdefer self.event_loop_local.deinit(); errdefer self.event_loop_local.deinit();
self.group = std.event.Group(error!void).init(&self.loop); self.group = std.event.Group(error!void).init(&self.loop);
@ -55,16 +55,12 @@ pub const TestContext = struct {
self.zig_lib_dir = try introspect.resolveZigLibDir(allocator); self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
errdefer allocator.free(self.zig_lib_dir); errdefer allocator.free(self.zig_lib_dir);
self.zig_cache_dir = try introspect.resolveZigCacheDir(allocator);
errdefer allocator.free(self.zig_cache_dir);
try std.os.makePath(allocator, tmp_dir_name); try std.os.makePath(allocator, tmp_dir_name);
errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {}; errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {};
} }
fn deinit(self: *TestContext) void { fn deinit(self: *TestContext) void {
std.os.deleteTree(allocator, tmp_dir_name) catch {}; std.os.deleteTree(allocator, tmp_dir_name) catch {};
allocator.free(self.zig_cache_dir);
allocator.free(self.zig_lib_dir); allocator.free(self.zig_lib_dir);
self.event_loop_local.deinit(); self.event_loop_local.deinit();
self.loop.deinit(); self.loop.deinit();
@ -107,8 +103,8 @@ pub const TestContext = struct {
Target.Native, Target.Native,
Compilation.Kind.Obj, Compilation.Kind.Obj,
builtin.Mode.Debug, builtin.Mode.Debug,
true, // is_static
self.zig_lib_dir, self.zig_lib_dir,
self.zig_cache_dir,
); );
errdefer comp.destroy(); errdefer comp.destroy();
@ -117,6 +113,84 @@ pub const TestContext = struct {
try self.group.call(getModuleEvent, comp, source, path, line, column, msg); try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
} }
fn testCompareOutputLibC(
self: *TestContext,
source: []const u8,
expected_output: []const u8,
) !void {
var file_index_buf: [20]u8 = undefined;
const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt());
if (std.os.path.dirname(file1_path)) |dirname| {
try std.os.makePath(allocator, dirname);
}
// TODO async I/O
try std.io.writeFile(allocator, file1_path, source);
var comp = try Compilation.create(
&self.event_loop_local,
"test",
file1_path,
Target.Native,
Compilation.Kind.Exe,
builtin.Mode.Debug,
false,
self.zig_lib_dir,
);
errdefer comp.destroy();
_ = try comp.addLinkLib("c", true);
comp.link_out_file = output_file;
try comp.build();
try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
}
async fn getModuleEventSuccess(
comp: *Compilation,
exe_file: []const u8,
expected_output: []const u8,
) !void {
// TODO this should not be necessary
const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file);
defer comp.destroy();
const build_event = await (async comp.events.get() catch unreachable);
switch (build_event) {
Compilation.Event.Ok => {
const argv = []const []const u8{exe_file_2};
// TODO use event loop
const child = try std.os.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024);
switch (child.term) {
std.os.ChildProcess.Term.Exited => |code| {
if (code != 0) {
return error.BadReturnCode;
}
},
else => {
return error.Crashed;
},
}
if (!mem.eql(u8, child.stdout, expected_output)) {
return error.OutputMismatch;
}
},
Compilation.Event.Error => |err| return err,
Compilation.Event.Fail => |msgs| {
var stderr = try std.io.getStdErr();
try stderr.write("build incorrectly failed:\n");
for (msgs) |msg| {
defer msg.destroy();
try msg.printToFile(&stderr, errmsg.Color.Auto);
}
},
}
}
async fn getModuleEvent( async fn getModuleEvent(
comp: *Compilation, comp: *Compilation,
source: []const u8, source: []const u8,
@ -138,10 +212,10 @@ pub const TestContext = struct {
Compilation.Event.Fail => |msgs| { Compilation.Event.Fail => |msgs| {
assertOrPanic(msgs.len != 0); assertOrPanic(msgs.len != 0);
for (msgs) |msg| { for (msgs) |msg| {
if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) { if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {
const first_token = msg.tree.tokens.at(msg.span.first); const first_token = msg.getTree().tokens.at(msg.span.first);
const last_token = msg.tree.tokens.at(msg.span.first); const last_token = msg.getTree().tokens.at(msg.span.first);
const start_loc = msg.tree.tokenLocationPtr(0, first_token); const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
if (start_loc.line + 1 == line and start_loc.column + 1 == column) { if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
return; return;
} }
@ -158,7 +232,8 @@ pub const TestContext = struct {
std.debug.warn("\n====found:========\n"); std.debug.warn("\n====found:========\n");
var stderr = try std.io.getStdErr(); var stderr = try std.io.getStdErr();
for (msgs) |msg| { for (msgs) |msg| {
try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto); defer msg.destroy();
try msg.printToFile(&stderr, errmsg.Color.Auto);
} }
std.debug.warn("============\n"); std.debug.warn("============\n");
return error.TestFailed; return error.TestFailed;

View file

@ -4,11 +4,17 @@ const Scope = @import("scope.zig").Scope;
const Compilation = @import("compilation.zig").Compilation; const Compilation = @import("compilation.zig").Compilation;
const Value = @import("value.zig").Value; const Value = @import("value.zig").Value;
const llvm = @import("llvm.zig"); const llvm = @import("llvm.zig");
const ObjectFile = @import("codegen.zig").ObjectFile; const event = std.event;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
pub const Type = struct { pub const Type = struct {
base: Value, base: Value,
id: Id, id: Id,
name: []const u8,
abi_alignment: AbiAlignment,
pub const AbiAlignment = event.Future(error{OutOfMemory}!u32);
pub const Id = builtin.TypeId; pub const Id = builtin.TypeId;
@ -42,33 +48,37 @@ pub const Type = struct {
} }
} }
pub fn getLlvmType(base: *Type, ofile: *ObjectFile) (error{OutOfMemory}!llvm.TypeRef) { pub fn getLlvmType(
base: *Type,
allocator: *Allocator,
llvm_context: llvm.ContextRef,
) (error{OutOfMemory}!llvm.TypeRef) {
switch (base.id) { switch (base.id) {
Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(ofile), Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(ofile), Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
Id.Type => unreachable, Id.Type => unreachable,
Id.Void => unreachable, Id.Void => unreachable,
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(ofile), Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(allocator, llvm_context),
Id.NoReturn => unreachable, Id.NoReturn => unreachable,
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(ofile), Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context),
Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(ofile), Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context),
Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(ofile), Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context),
Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(ofile), Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context),
Id.ComptimeFloat => unreachable, Id.ComptimeFloat => unreachable,
Id.ComptimeInt => unreachable, Id.ComptimeInt => unreachable,
Id.Undefined => unreachable, Id.Undefined => unreachable,
Id.Null => unreachable, Id.Null => unreachable,
Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(ofile), Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context),
Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(ofile), Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context),
Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(ofile), Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context),
Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(ofile), Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context),
Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(ofile), Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context),
Id.Namespace => unreachable, Id.Namespace => unreachable,
Id.Block => unreachable, Id.Block => unreachable,
Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(ofile), Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context),
Id.ArgTuple => unreachable, Id.ArgTuple => unreachable,
Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(ofile), Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context),
Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(ofile), Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context),
} }
} }
@ -151,8 +161,49 @@ pub const Type = struct {
std.debug.warn("{}", @tagName(base.id)); std.debug.warn("{}", @tagName(base.id));
} }
pub fn getAbiAlignment(base: *Type, comp: *Compilation) u32 { fn init(base: *Type, comp: *Compilation, id: Id, name: []const u8) void {
@panic("TODO getAbiAlignment"); base.* = Type{
.base = Value{
.id = Value.Id.Type,
.typ = &MetaType.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
.id = id,
.name = name,
.abi_alignment = AbiAlignment.init(comp.loop),
};
}
/// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead.
/// Otherwise, this one will grab one from the pool and then release it.
pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
{
const held = try comp.event_loop_local.getAnyLlvmContext();
defer held.release(comp.event_loop_local);
const llvm_context = held.node.data;
base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
}
base.abi_alignment.resolve();
return base.abi_alignment.data;
}
/// If you have an llvm conext handy, you can use it here.
pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
base.abi_alignment.resolve();
return base.abi_alignment.data;
}
/// Lower level function that does the work. See getAbiAlignment.
async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
} }
pub const Struct = struct { pub const Struct = struct {
@ -160,10 +211,10 @@ pub const Type = struct {
decls: *Scope.Decls, decls: *Scope.Decls,
pub fn destroy(self: *Struct, comp: *Compilation) void { pub fn destroy(self: *Struct, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef { pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO"); @panic("TODO");
} }
}; };
@ -176,28 +227,23 @@ pub const Type = struct {
pub const Param = struct { pub const Param = struct {
is_noalias: bool, is_noalias: bool,
typeof: *Type, typ: *Type,
}; };
pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
const result = try comp.a().create(Fn{ const result = try comp.gpa().create(Fn{
.base = Type{ .base = undefined,
.base = Value{
.id = Value.Id.Type,
.typeof = &MetaType.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
.id = builtin.TypeId.Fn,
},
.return_type = return_type, .return_type = return_type,
.params = params, .params = params,
.is_var_args = is_var_args, .is_var_args = is_var_args,
}); });
errdefer comp.a().destroy(result); errdefer comp.gpa().destroy(result);
result.base.init(comp, Id.Fn, "TODO fn type name");
result.return_type.base.ref(); result.return_type.base.ref();
for (result.params) |param| { for (result.params) |param| {
param.typeof.base.ref(); param.typ.base.ref();
} }
return result; return result;
} }
@ -205,20 +251,20 @@ pub const Type = struct {
pub fn destroy(self: *Fn, comp: *Compilation) void { pub fn destroy(self: *Fn, comp: *Compilation) void {
self.return_type.base.deref(comp); self.return_type.base.deref(comp);
for (self.params) |param| { for (self.params) |param| {
param.typeof.base.deref(comp); param.typ.base.deref(comp);
} }
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef { pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
const llvm_return_type = switch (self.return_type.id) { const llvm_return_type = switch (self.return_type.id) {
Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory, Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
else => try self.return_type.getLlvmType(ofile), else => try self.return_type.getLlvmType(allocator, llvm_context),
}; };
const llvm_param_types = try ofile.a().alloc(llvm.TypeRef, self.params.len); const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len);
defer ofile.a().free(llvm_param_types); defer allocator.free(llvm_param_types);
for (llvm_param_types) |*llvm_param_type, i| { for (llvm_param_types) |*llvm_param_type, i| {
llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile); llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context);
} }
return llvm.FunctionType( return llvm.FunctionType(
@ -241,7 +287,7 @@ pub const Type = struct {
} }
pub fn destroy(self: *MetaType, comp: *Compilation) void { pub fn destroy(self: *MetaType, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -255,7 +301,7 @@ pub const Type = struct {
} }
pub fn destroy(self: *Void, comp: *Compilation) void { pub fn destroy(self: *Void, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -269,10 +315,10 @@ pub const Type = struct {
} }
pub fn destroy(self: *Bool, comp: *Compilation) void { pub fn destroy(self: *Bool, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef { pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO"); @panic("TODO");
} }
}; };
@ -287,19 +333,89 @@ pub const Type = struct {
} }
pub fn destroy(self: *NoReturn, comp: *Compilation) void { pub fn destroy(self: *NoReturn, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
pub const Int = struct { pub const Int = struct {
base: Type, base: Type,
key: Key,
garbage_node: std.atomic.Stack(*Int).Node,
pub fn destroy(self: *Int, comp: *Compilation) void { pub const Key = struct {
comp.a().destroy(self); bit_count: u32,
is_signed: bool,
pub fn hash(self: *const Key) u32 {
const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 };
return rands[@boolToInt(self.is_signed)] *% self.bit_count;
}
pub fn eql(self: *const Key, other: *const Key) bool {
return self.bit_count == other.bit_count and self.is_signed == other.is_signed;
}
};
pub fn get_u8(comp: *Compilation) *Int {
comp.u8_type.base.base.ref();
return comp.u8_type;
} }
pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef { pub async fn get(comp: *Compilation, key: Key) !*Int {
@panic("TODO"); {
const held = await (async comp.int_type_table.acquire() catch unreachable);
defer held.release();
if (held.value.get(&key)) |entry| {
entry.value.base.base.ref();
return entry.value;
}
}
const self = try comp.gpa().create(Int{
.base = undefined,
.key = key,
.garbage_node = undefined,
});
errdefer comp.gpa().destroy(self);
const u_or_i = "ui"[@boolToInt(key.is_signed)];
const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", u_or_i, key.bit_count);
errdefer comp.gpa().free(name);
self.base.init(comp, Id.Int, name);
{
const held = await (async comp.int_type_table.acquire() catch unreachable);
defer held.release();
_ = try held.value.put(&self.key, self);
}
return self;
}
pub fn destroy(self: *Int, comp: *Compilation) void {
self.garbage_node = std.atomic.Stack(*Int).Node{
.data = self,
.next = undefined,
};
comp.registerGarbage(Int, &self.garbage_node);
}
pub async fn gcDestroy(self: *Int, comp: *Compilation) void {
{
const held = await (async comp.int_type_table.acquire() catch unreachable);
defer held.release();
_ = held.value.remove(&self.key).?;
}
// we allocated the name
comp.gpa().free(self.base.name);
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory;
} }
}; };
@ -307,59 +423,239 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *Float, comp: *Compilation) void { pub fn destroy(self: *Float, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef { pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO"); @panic("TODO");
} }
}; };
pub const Pointer = struct { pub const Pointer = struct {
base: Type, base: Type,
mut: Mut, key: Key,
vol: Vol, garbage_node: std.atomic.Stack(*Pointer).Node,
size: Size,
alignment: u32, pub const Key = struct {
child_type: *Type,
mut: Mut,
vol: Vol,
size: Size,
alignment: Align,
pub fn hash(self: *const Key) u32 {
const align_hash = switch (self.alignment) {
Align.Abi => 0xf201c090,
Align.Override => |x| x,
};
return hash_usize(@ptrToInt(self.child_type)) *%
hash_enum(self.mut) *%
hash_enum(self.vol) *%
hash_enum(self.size) *%
align_hash;
}
pub fn eql(self: *const Key, other: *const Key) bool {
if (self.child_type != other.child_type or
self.mut != other.mut or
self.vol != other.vol or
self.size != other.size or
@TagType(Align)(self.alignment) != @TagType(Align)(other.alignment))
{
return false;
}
switch (self.alignment) {
Align.Abi => return true,
Align.Override => |x| return x == other.alignment.Override,
}
}
};
pub const Mut = enum { pub const Mut = enum {
Mut, Mut,
Const, Const,
}; };
pub const Vol = enum { pub const Vol = enum {
Non, Non,
Volatile, Volatile,
}; };
pub const Align = union(enum) {
Abi,
Override: u32,
};
pub const Size = builtin.TypeInfo.Pointer.Size; pub const Size = builtin.TypeInfo.Pointer.Size;
pub fn destroy(self: *Pointer, comp: *Compilation) void { pub fn destroy(self: *Pointer, comp: *Compilation) void {
comp.a().destroy(self); self.garbage_node = std.atomic.Stack(*Pointer).Node{
.data = self,
.next = undefined,
};
comp.registerGarbage(Pointer, &self.garbage_node);
} }
pub fn get( pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void {
{
const held = await (async comp.ptr_type_table.acquire() catch unreachable);
defer held.release();
_ = held.value.remove(&self.key).?;
}
self.key.child_type.base.deref(comp);
comp.gpa().destroy(self);
}
pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
switch (self.key.alignment) {
Align.Abi => return await (async self.key.child_type.getAbiAlignment(comp) catch unreachable),
Align.Override => |alignment| return alignment,
}
}
pub async fn get(
comp: *Compilation, comp: *Compilation,
elem_type: *Type, key: Key,
mut: Mut, ) !*Pointer {
vol: Vol, var normal_key = key;
size: Size, switch (key.alignment) {
alignment: u32, Align.Abi => {},
) *Pointer { Align.Override => |alignment| {
@panic("TODO get pointer"); const abi_align = try await (async key.child_type.getAbiAlignment(comp) catch unreachable);
if (abi_align == alignment) {
normal_key.alignment = Align.Abi;
}
},
}
{
const held = await (async comp.ptr_type_table.acquire() catch unreachable);
defer held.release();
if (held.value.get(&normal_key)) |entry| {
entry.value.base.base.ref();
return entry.value;
}
}
const self = try comp.gpa().create(Pointer{
.base = undefined,
.key = normal_key,
.garbage_node = undefined,
});
errdefer comp.gpa().destroy(self);
const size_str = switch (self.key.size) {
Size.One => "*",
Size.Many => "[*]",
Size.Slice => "[]",
};
const mut_str = switch (self.key.mut) {
Mut.Const => "const ",
Mut.Mut => "",
};
const vol_str = switch (self.key.vol) {
Vol.Volatile => "volatile ",
Vol.Non => "",
};
const name = switch (self.key.alignment) {
Align.Abi => try std.fmt.allocPrint(
comp.gpa(),
"{}{}{}{}",
size_str,
mut_str,
vol_str,
self.key.child_type.name,
),
Align.Override => |alignment| try std.fmt.allocPrint(
comp.gpa(),
"{}align<{}> {}{}{}",
size_str,
alignment,
mut_str,
vol_str,
self.key.child_type.name,
),
};
errdefer comp.gpa().free(name);
self.base.init(comp, Id.Pointer, name);
{
const held = await (async comp.ptr_type_table.acquire() catch unreachable);
defer held.release();
_ = try held.value.put(&self.key, self);
}
return self;
} }
pub fn getLlvmType(self: *Pointer, ofile: *ObjectFile) llvm.TypeRef { pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
@panic("TODO"); const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context);
return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory;
} }
}; };
pub const Array = struct { pub const Array = struct {
base: Type, base: Type,
key: Key,
garbage_node: std.atomic.Stack(*Array).Node,
pub const Key = struct {
elem_type: *Type,
len: usize,
pub fn hash(self: *const Key) u32 {
return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len);
}
pub fn eql(self: *const Key, other: *const Key) bool {
return self.elem_type == other.elem_type and self.len == other.len;
}
};
pub fn destroy(self: *Array, comp: *Compilation) void { pub fn destroy(self: *Array, comp: *Compilation) void {
comp.a().destroy(self); self.key.elem_type.base.deref(comp);
comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef { pub async fn get(comp: *Compilation, key: Key) !*Array {
@panic("TODO"); key.elem_type.base.ref();
errdefer key.elem_type.base.deref(comp);
{
const held = await (async comp.array_type_table.acquire() catch unreachable);
defer held.release();
if (held.value.get(&key)) |entry| {
entry.value.base.base.ref();
return entry.value;
}
}
const self = try comp.gpa().create(Array{
.base = undefined,
.key = key,
.garbage_node = undefined,
});
errdefer comp.gpa().destroy(self);
const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", key.len, key.elem_type.name);
errdefer comp.gpa().free(name);
self.base.init(comp, Id.Array, name);
{
const held = await (async comp.array_type_table.acquire() catch unreachable);
defer held.release();
_ = try held.value.put(&self.key, self);
}
return self;
}
pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context);
return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory;
} }
}; };
@ -367,15 +663,21 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void { pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
pub const ComptimeInt = struct { pub const ComptimeInt = struct {
base: Type, base: Type,
/// Adds 1 reference to the resulting type
pub fn get(comp: *Compilation) *ComptimeInt {
comp.comptime_int_type.base.base.ref();
return comp.comptime_int_type;
}
pub fn destroy(self: *ComptimeInt, comp: *Compilation) void { pub fn destroy(self: *ComptimeInt, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -383,7 +685,7 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *Undefined, comp: *Compilation) void { pub fn destroy(self: *Undefined, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -391,7 +693,7 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *Null, comp: *Compilation) void { pub fn destroy(self: *Null, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -399,10 +701,10 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *Optional, comp: *Compilation) void { pub fn destroy(self: *Optional, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef { pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO"); @panic("TODO");
} }
}; };
@ -411,10 +713,10 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *ErrorUnion, comp: *Compilation) void { pub fn destroy(self: *ErrorUnion, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef { pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO"); @panic("TODO");
} }
}; };
@ -423,10 +725,10 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *ErrorSet, comp: *Compilation) void { pub fn destroy(self: *ErrorSet, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef { pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO"); @panic("TODO");
} }
}; };
@ -435,10 +737,10 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *Enum, comp: *Compilation) void { pub fn destroy(self: *Enum, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef { pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO"); @panic("TODO");
} }
}; };
@ -447,10 +749,10 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *Union, comp: *Compilation) void { pub fn destroy(self: *Union, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef { pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO"); @panic("TODO");
} }
}; };
@ -459,7 +761,7 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *Namespace, comp: *Compilation) void { pub fn destroy(self: *Namespace, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -467,7 +769,7 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *Block, comp: *Compilation) void { pub fn destroy(self: *Block, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -475,10 +777,10 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *BoundFn, comp: *Compilation) void { pub fn destroy(self: *BoundFn, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef { pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO"); @panic("TODO");
} }
}; };
@ -487,7 +789,7 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *ArgTuple, comp: *Compilation) void { pub fn destroy(self: *ArgTuple, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -495,10 +797,10 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *Opaque, comp: *Compilation) void { pub fn destroy(self: *Opaque, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef { pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO"); @panic("TODO");
} }
}; };
@ -507,11 +809,36 @@ pub const Type = struct {
base: Type, base: Type,
pub fn destroy(self: *Promise, comp: *Compilation) void { pub fn destroy(self: *Promise, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef { pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO"); @panic("TODO");
} }
}; };
}; };
fn hash_usize(x: usize) u32 {
return switch (@sizeOf(usize)) {
4 => x,
8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d),
else => @compileError("implement this hash function"),
};
}
fn hash_enum(x: var) u32 {
const rands = []u32{
0x85ebf64f,
0x3fcb3211,
0x240a4e8e,
0x40bb0e3c,
0x78be45af,
0x1ca98e37,
0xec56053a,
0x906adc48,
0xd4fe9763,
0x54c80dac,
};
comptime assert(@memberCount(@typeOf(x)) < rands.len);
return rands[@enumToInt(x)];
}

View file

@ -4,12 +4,14 @@ const Scope = @import("scope.zig").Scope;
const Compilation = @import("compilation.zig").Compilation; const Compilation = @import("compilation.zig").Compilation;
const ObjectFile = @import("codegen.zig").ObjectFile; const ObjectFile = @import("codegen.zig").ObjectFile;
const llvm = @import("llvm.zig"); const llvm = @import("llvm.zig");
const Buffer = std.Buffer;
const assert = std.debug.assert;
/// Values are ref-counted, heap-allocated, and copy-on-write /// Values are ref-counted, heap-allocated, and copy-on-write
/// If there is only 1 ref then write need not copy /// If there is only 1 ref then write need not copy
pub const Value = struct { pub const Value = struct {
id: Id, id: Id,
typeof: *Type, typ: *Type,
ref_count: std.atomic.Int(usize), ref_count: std.atomic.Int(usize),
/// Thread-safe /// Thread-safe
@ -20,23 +22,37 @@ pub const Value = struct {
/// Thread-safe /// Thread-safe
pub fn deref(base: *Value, comp: *Compilation) void { pub fn deref(base: *Value, comp: *Compilation) void {
if (base.ref_count.decr() == 1) { if (base.ref_count.decr() == 1) {
base.typeof.base.deref(comp); base.typ.base.deref(comp);
switch (base.id) { switch (base.id) {
Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp), Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp),
Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp), Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
Id.FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp),
Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp), Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp),
Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp), Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp), Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp), Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp),
Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp),
Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp),
} }
} }
} }
pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void {
base.typ.base.deref(comp);
new_type.base.ref();
base.typ = new_type;
}
pub fn getRef(base: *Value) *Value { pub fn getRef(base: *Value) *Value {
base.ref(); base.ref();
return base; return base;
} }
pub fn cast(base: *Value, comptime T: type) ?*T {
if (base.id != @field(Id, @typeName(T))) return null;
return @fieldParentPtr(T, "base", base);
}
pub fn dump(base: *const Value) void { pub fn dump(base: *const Value) void {
std.debug.warn("{}", @tagName(base.id)); std.debug.warn("{}", @tagName(base.id));
} }
@ -45,30 +61,117 @@ pub const Value = struct {
switch (base.id) { switch (base.id) {
Id.Type => unreachable, Id.Type => unreachable,
Id.Fn => @panic("TODO"), Id.Fn => @panic("TODO"),
Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
Id.Void => return null, Id.Void => return null,
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile), Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
Id.NoReturn => unreachable, Id.NoReturn => unreachable,
Id.Ptr => @panic("TODO"), Id.Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile),
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile),
Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile),
} }
} }
pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
if (self.ref_count.get() == 1) {
// ( ͡° ͜ʖ ͡°)
return self;
}
assert(self.ref_count.decr() != 1);
return self.copy(comp);
}
pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
switch (base.id) {
Id.Type => unreachable,
Id.Fn => unreachable,
Id.FnProto => unreachable,
Id.Void => unreachable,
Id.Bool => unreachable,
Id.NoReturn => unreachable,
Id.Ptr => unreachable,
Id.Array => unreachable,
Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base,
}
}
pub const Parent = union(enum) {
None,
BaseStruct: BaseStruct,
BaseArray: BaseArray,
BaseUnion: *Value,
BaseScalar: *Value,
pub const BaseStruct = struct {
val: *Value,
field_index: usize,
};
pub const BaseArray = struct {
val: *Value,
elem_index: usize,
};
};
pub const Id = enum { pub const Id = enum {
Type, Type,
Fn, Fn,
Void, Void,
Bool, Bool,
NoReturn, NoReturn,
Array,
Ptr, Ptr,
Int,
FnProto,
}; };
pub const Type = @import("type.zig").Type; pub const Type = @import("type.zig").Type;
pub const FnProto = struct {
base: Value,
/// The main external name that is used in the .o file.
/// TODO https://github.com/ziglang/zig/issues/265
symbol_name: Buffer,
pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto {
const self = try comp.gpa().create(FnProto{
.base = Value{
.id = Value.Id.FnProto,
.typ = &fn_type.base,
.ref_count = std.atomic.Int(usize).init(1),
},
.symbol_name = symbol_name,
});
fn_type.base.base.ref();
return self;
}
pub fn destroy(self: *FnProto, comp: *Compilation) void {
self.symbol_name.deinit();
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?llvm.ValueRef {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
self.symbol_name.ptr(),
llvm_fn_type,
) orelse return error.OutOfMemory;
// TODO port more logic from codegen.cpp:fn_llvm_value
return llvm_fn;
}
};
pub const Fn = struct { pub const Fn = struct {
base: Value, base: Value,
/// The main external name that is used in the .o file. /// The main external name that is used in the .o file.
/// TODO https://github.com/ziglang/zig/issues/265 /// TODO https://github.com/ziglang/zig/issues/265
symbol_name: std.Buffer, symbol_name: Buffer,
/// parent should be the top level decls or container decls /// parent should be the top level decls or container decls
fndef_scope: *Scope.FnDef, fndef_scope: *Scope.FnDef,
@ -79,19 +182,33 @@ pub const Value = struct {
/// parent is child_scope /// parent is child_scope
block_scope: *Scope.Block, block_scope: *Scope.Block,
/// Path to the object file that contains this function
containing_object: Buffer,
link_set_node: *std.LinkedList(?*Value.Fn).Node,
/// Creates a Fn value with 1 ref /// Creates a Fn value with 1 ref
/// Takes ownership of symbol_name /// Takes ownership of symbol_name
pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn { pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn {
const self = try comp.a().create(Fn{ const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{
.data = null,
.next = undefined,
.prev = undefined,
});
errdefer comp.gpa().destroy(link_set_node);
const self = try comp.gpa().create(Fn{
.base = Value{ .base = Value{
.id = Value.Id.Fn, .id = Value.Id.Fn,
.typeof = &fn_type.base, .typ = &fn_type.base,
.ref_count = std.atomic.Int(usize).init(1), .ref_count = std.atomic.Int(usize).init(1),
}, },
.fndef_scope = fndef_scope, .fndef_scope = fndef_scope,
.child_scope = &fndef_scope.base, .child_scope = &fndef_scope.base,
.block_scope = undefined, .block_scope = undefined,
.symbol_name = symbol_name, .symbol_name = symbol_name,
.containing_object = Buffer.initNull(comp.gpa()),
.link_set_node = link_set_node,
}); });
fn_type.base.base.ref(); fn_type.base.base.ref();
fndef_scope.fn_val = self; fndef_scope.fn_val = self;
@ -100,9 +217,19 @@ pub const Value = struct {
} }
pub fn destroy(self: *Fn, comp: *Compilation) void { pub fn destroy(self: *Fn, comp: *Compilation) void {
// remove with a tombstone so that we do not have to grab a lock
if (self.link_set_node.data != null) {
// it's now the job of the link step to find this tombstone and
// deallocate it.
self.link_set_node.data = null;
} else {
comp.gpa().destroy(self.link_set_node);
}
self.containing_object.deinit();
self.fndef_scope.base.deref(comp); self.fndef_scope.base.deref(comp);
self.symbol_name.deinit(); self.symbol_name.deinit();
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -115,7 +242,7 @@ pub const Value = struct {
} }
pub fn destroy(self: *Void, comp: *Compilation) void { pub fn destroy(self: *Void, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
@ -134,7 +261,7 @@ pub const Value = struct {
} }
pub fn destroy(self: *Bool, comp: *Compilation) void { pub fn destroy(self: *Bool, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef { pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef {
@ -156,12 +283,14 @@ pub const Value = struct {
} }
pub fn destroy(self: *NoReturn, comp: *Compilation) void { pub fn destroy(self: *NoReturn, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
} }
}; };
pub const Ptr = struct { pub const Ptr = struct {
base: Value, base: Value,
special: Special,
mut: Mut,
pub const Mut = enum { pub const Mut = enum {
CompTimeConst, CompTimeConst,
@ -169,8 +298,268 @@ pub const Value = struct {
RunTime, RunTime,
}; };
pub const Special = union(enum) {
Scalar: *Value,
BaseArray: BaseArray,
BaseStruct: BaseStruct,
HardCodedAddr: u64,
Discard,
};
pub const BaseArray = struct {
val: *Value,
elem_index: usize,
};
pub const BaseStruct = struct {
val: *Value,
field_index: usize,
};
pub async fn createArrayElemPtr(
comp: *Compilation,
array_val: *Array,
mut: Type.Pointer.Mut,
size: Type.Pointer.Size,
elem_index: usize,
) !*Ptr {
array_val.base.ref();
errdefer array_val.base.deref(comp);
const elem_type = array_val.base.typ.cast(Type.Array).?.key.elem_type;
const ptr_type = try await (async Type.Pointer.get(comp, Type.Pointer.Key{
.child_type = elem_type,
.mut = mut,
.vol = Type.Pointer.Vol.Non,
.size = size,
.alignment = Type.Pointer.Align.Abi,
}) catch unreachable);
var ptr_type_consumed = false;
errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
const self = try comp.gpa().create(Value.Ptr{
.base = Value{
.id = Value.Id.Ptr,
.typ = &ptr_type.base,
.ref_count = std.atomic.Int(usize).init(1),
},
.special = Special{
.BaseArray = BaseArray{
.val = &array_val.base,
.elem_index = 0,
},
},
.mut = Mut.CompTimeConst,
});
ptr_type_consumed = true;
errdefer comp.gpa().destroy(self);
return self;
}
pub fn destroy(self: *Ptr, comp: *Compilation) void { pub fn destroy(self: *Ptr, comp: *Compilation) void {
comp.a().destroy(self); comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?llvm.ValueRef {
const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context);
// TODO carefully port the logic from codegen.cpp:gen_const_val_ptr
switch (self.special) {
Special.Scalar => |scalar| @panic("TODO"),
Special.BaseArray => |base_array| {
// TODO put this in one .o file only, and after that, generate extern references to it
const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
const ptr_bit_count = ofile.comp.target_ptr_bits;
const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
const indices = []llvm.ValueRef{
llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
};
return llvm.ConstInBoundsGEP(
array_llvm_value,
&indices,
@intCast(c_uint, indices.len),
) orelse return error.OutOfMemory;
},
Special.BaseStruct => |base_struct| @panic("TODO"),
Special.HardCodedAddr => |addr| @panic("TODO"),
Special.Discard => unreachable,
}
}
};
pub const Array = struct {
base: Value,
special: Special,
pub const Special = union(enum) {
Undefined,
OwnedBuffer: []u8,
Explicit: Data,
};
pub const Data = struct {
parent: Parent,
elements: []*Value,
};
/// Takes ownership of buffer
pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
const u8_type = Type.Int.get_u8(comp);
defer u8_type.base.base.deref(comp);
const array_type = try await (async Type.Array.get(comp, Type.Array.Key{
.elem_type = &u8_type.base,
.len = buffer.len,
}) catch unreachable);
errdefer array_type.base.base.deref(comp);
const self = try comp.gpa().create(Value.Array{
.base = Value{
.id = Value.Id.Array,
.typ = &array_type.base,
.ref_count = std.atomic.Int(usize).init(1),
},
.special = Special{ .OwnedBuffer = buffer },
});
errdefer comp.gpa().destroy(self);
return self;
}
pub fn destroy(self: *Array, comp: *Compilation) void {
switch (self.special) {
Special.Undefined => {},
Special.OwnedBuffer => |buf| {
comp.gpa().free(buf);
},
Special.Explicit => {},
}
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?llvm.ValueRef {
switch (self.special) {
Special.Undefined => {
const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
return llvm.GetUndef(llvm_type);
},
Special.OwnedBuffer => |buf| {
const dont_null_terminate = 1;
const llvm_str_init = llvm.ConstStringInContext(
ofile.context,
buf.ptr,
@intCast(c_uint, buf.len),
dont_null_terminate,
) orelse return error.OutOfMemory;
const str_init_type = llvm.TypeOf(llvm_str_init);
const global = llvm.AddGlobal(ofile.module, str_init_type, c"") orelse return error.OutOfMemory;
llvm.SetInitializer(global, llvm_str_init);
llvm.SetLinkage(global, llvm.PrivateLinkage);
llvm.SetGlobalConstant(global, 1);
llvm.SetUnnamedAddr(global, 1);
llvm.SetAlignment(global, llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, str_init_type));
return global;
},
Special.Explicit => @panic("TODO"),
}
//{
// uint64_t len = type_entry->data.array.len;
// if (const_val->data.x_array.special == ConstArraySpecialUndef) {
// return LLVMGetUndef(type_entry->type_ref);
// }
// LLVMValueRef *values = allocate<LLVMValueRef>(len);
// LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref;
// bool make_unnamed_struct = false;
// for (uint64_t i = 0; i < len; i += 1) {
// ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
// LLVMValueRef val = gen_const_val(g, elem_value, "");
// values[i] = val;
// make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
// }
// if (make_unnamed_struct) {
// return LLVMConstStruct(values, len, true);
// } else {
// return LLVMConstArray(element_type_ref, values, (unsigned)len);
// }
//}
}
};
pub const Int = struct {
base: Value,
big_int: std.math.big.Int,
pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int {
const self = try comp.gpa().create(Value.Int{
.base = Value{
.id = Value.Id.Int,
.typ = typ,
.ref_count = std.atomic.Int(usize).init(1),
},
.big_int = undefined,
});
typ.base.ref();
errdefer comp.gpa().destroy(self);
self.big_int = try std.math.big.Int.init(comp.gpa());
errdefer self.big_int.deinit();
try self.big_int.setString(base, value);
return self;
}
pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef {
switch (self.base.typ.id) {
Type.Id.Int => {
const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
if (self.big_int.len == 0) {
return llvm.ConstNull(type_ref);
}
const unsigned_val = if (self.big_int.len == 1) blk: {
break :blk llvm.ConstInt(type_ref, self.big_int.limbs[0], @boolToInt(false));
} else if (@sizeOf(std.math.big.Limb) == @sizeOf(u64)) blk: {
break :blk llvm.ConstIntOfArbitraryPrecision(
type_ref,
@intCast(c_uint, self.big_int.len),
@ptrCast([*]u64, self.big_int.limbs.ptr),
);
} else {
@compileError("std.math.Big.Int.Limb size does not match LLVM");
};
return if (self.big_int.positive) unsigned_val else llvm.ConstNeg(unsigned_val);
},
Type.Id.ComptimeInt => unreachable,
else => unreachable,
}
}
pub fn copy(old: *Int, comp: *Compilation) !*Int {
old.base.typ.base.ref();
errdefer old.base.typ.base.deref(comp);
const new = try comp.gpa().create(Value.Int{
.base = Value{
.id = Value.Id.Int,
.typ = old.base.typ,
.ref_count = std.atomic.Int(usize).init(1),
},
.big_int = undefined,
});
errdefer comp.gpa().destroy(new);
new.big_int = try old.big_int.clone();
errdefer new.big_int.deinit();
return new;
}
pub fn destroy(self: *Int, comp: *Compilation) void {
self.big_int.deinit();
comp.gpa().destroy(self);
} }
}; };
}; };

View file

@ -1454,7 +1454,9 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
case TypeTableEntryIdFn: case TypeTableEntryIdFn:
return type_entry->data.fn.fn_type_id.cc == CallingConventionC; return type_entry->data.fn.fn_type_id.cc == CallingConventionC;
case TypeTableEntryIdPointer: case TypeTableEntryIdPointer:
return type_allowed_in_extern(g, type_entry->data.pointer.child_type); if (type_size(g, type_entry) == 0)
return false;
return true;
case TypeTableEntryIdStruct: case TypeTableEntryIdStruct:
return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked; return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked;
case TypeTableEntryIdOptional: case TypeTableEntryIdOptional:
@ -4377,7 +4379,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
static ZigWindowsSDK *get_windows_sdk(CodeGen *g) { static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {
if (g->win_sdk == nullptr) { if (g->win_sdk == nullptr) {
if (os_find_windows_sdk(&g->win_sdk)) { if (zig_find_windows_sdk(&g->win_sdk)) {
fprintf(stderr, "unable to determine windows sdk path\n"); fprintf(stderr, "unable to determine windows sdk path\n");
exit(1); exit(1);
} }
@ -4497,12 +4499,11 @@ void find_libc_lib_path(CodeGen *g) {
ZigWindowsSDK *sdk = get_windows_sdk(g); ZigWindowsSDK *sdk = get_windows_sdk(g);
if (g->msvc_lib_dir == nullptr) { if (g->msvc_lib_dir == nullptr) {
Buf* vc_lib_dir = buf_alloc(); if (sdk->msvc_lib_dir_ptr == nullptr) {
if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {
fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir"); fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir");
exit(1); exit(1);
} }
g->msvc_lib_dir = vc_lib_dir; g->msvc_lib_dir = buf_create_from_mem(sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len);
} }
if (g->libc_lib_dir == nullptr) { if (g->libc_lib_dir == nullptr) {

View file

@ -60,6 +60,33 @@ PackageTableEntry *new_anonymous_package(void) {
return new_package("", ""); return new_package("", "");
} }
static const char *symbols_that_llvm_depends_on[] = {
"memcpy",
"memset",
"sqrt",
"powi",
"sin",
"cos",
"pow",
"exp",
"exp2",
"log",
"log10",
"log2",
"fma",
"fabs",
"minnum",
"maxnum",
"copysign",
"floor",
"ceil",
"trunc",
"rint",
"nearbyint",
"round",
// TODO probably all of compiler-rt needs to go here
};
CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,
Buf *zig_lib_dir) Buf *zig_lib_dir)
{ {
@ -94,6 +121,10 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib); g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);
buf_resize(&g->global_asm, 0); buf_resize(&g->global_asm, 0);
for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) {
g->external_prototypes.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr);
}
if (root_src_path) { if (root_src_path) {
Buf *src_basename = buf_alloc(); Buf *src_basename = buf_alloc();
Buf *src_dir = buf_alloc(); Buf *src_dir = buf_alloc();
@ -7419,51 +7450,60 @@ static void gen_h_file(CodeGen *g) {
case TypeTableEntryIdPromise: case TypeTableEntryIdPromise:
zig_unreachable(); zig_unreachable();
case TypeTableEntryIdEnum: case TypeTableEntryIdEnum:
assert(type_entry->data.enumeration.layout == ContainerLayoutExtern); if (type_entry->data.enumeration.layout == ContainerLayoutExtern) {
fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name)); fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name));
for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) { for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) {
TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i]; TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i];
Buf *value_buf = buf_alloc(); Buf *value_buf = buf_alloc();
bigint_append_buf(value_buf, &enum_field->value, 10); bigint_append_buf(value_buf, &enum_field->value, 10);
fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf));
if (field_i != type_entry->data.enumeration.src_field_count - 1) { if (field_i != type_entry->data.enumeration.src_field_count - 1) {
fprintf(out_h, ","); fprintf(out_h, ",");
}
fprintf(out_h, "\n");
} }
fprintf(out_h, "\n"); fprintf(out_h, "};\n\n");
} else {
fprintf(out_h, "enum %s;\n", buf_ptr(&type_entry->name));
} }
fprintf(out_h, "};\n\n");
break; break;
case TypeTableEntryIdStruct: case TypeTableEntryIdStruct:
assert(type_entry->data.structure.layout == ContainerLayoutExtern); if (type_entry->data.structure.layout == ContainerLayoutExtern) {
fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name)); fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name));
for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) { for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) {
TypeStructField *struct_field = &type_entry->data.structure.fields[field_i]; TypeStructField *struct_field = &type_entry->data.structure.fields[field_i];
Buf *type_name_buf = buf_alloc(); Buf *type_name_buf = buf_alloc();
get_c_type(g, gen_h, struct_field->type_entry, type_name_buf); get_c_type(g, gen_h, struct_field->type_entry, type_name_buf);
if (struct_field->type_entry->id == TypeTableEntryIdArray) {
fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf),
buf_ptr(struct_field->name),
struct_field->type_entry->data.array.len);
} else {
fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name));
}
if (struct_field->type_entry->id == TypeTableEntryIdArray) {
fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf),
buf_ptr(struct_field->name),
struct_field->type_entry->data.array.len);
} else {
fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name));
} }
fprintf(out_h, "};\n\n");
} else {
fprintf(out_h, "struct %s;\n", buf_ptr(&type_entry->name));
} }
fprintf(out_h, "};\n\n");
break; break;
case TypeTableEntryIdUnion: case TypeTableEntryIdUnion:
assert(type_entry->data.unionation.layout == ContainerLayoutExtern); if (type_entry->data.unionation.layout == ContainerLayoutExtern) {
fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name)); fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name));
for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) { for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) {
TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i]; TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i];
Buf *type_name_buf = buf_alloc(); Buf *type_name_buf = buf_alloc();
get_c_type(g, gen_h, union_field->type_entry, type_name_buf); get_c_type(g, gen_h, union_field->type_entry, type_name_buf);
fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name));
}
fprintf(out_h, "};\n\n");
} else {
fprintf(out_h, "union %s;\n", buf_ptr(&type_entry->name));
} }
fprintf(out_h, "};\n\n");
break; break;
case TypeTableEntryIdOpaque: case TypeTableEntryIdOpaque:
fprintf(out_h, "struct %s;\n\n", buf_ptr(&type_entry->name)); fprintf(out_h, "struct %s;\n\n", buf_ptr(&type_entry->name));

View file

@ -246,6 +246,8 @@ static void ir_ref_bb(IrBasicBlock *bb) {
static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) { static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) {
assert(instruction->id != IrInstructionIdInvalid); assert(instruction->id != IrInstructionIdInvalid);
instruction->ref_count += 1; instruction->ref_count += 1;
if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction))
ir_ref_bb(instruction->owner_bb);
} }
static void ir_ref_var(VariableTableEntry *var) { static void ir_ref_var(VariableTableEntry *var) {
@ -2959,16 +2961,34 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco
results[ReturnKindUnconditional] = 0; results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0; results[ReturnKindError] = 0;
while (inner_scope != outer_scope) { Scope *scope = inner_scope;
assert(inner_scope);
if (inner_scope->id == ScopeIdDefer) {
AstNode *defer_node = inner_scope->source_node;
assert(defer_node->type == NodeTypeDefer);
ReturnKind defer_kind = defer_node->data.defer.kind;
results[defer_kind] += 1;
while (scope != outer_scope) {
assert(scope);
switch (scope->id) {
case ScopeIdDefer: {
AstNode *defer_node = scope->source_node;
assert(defer_node->type == NodeTypeDefer);
ReturnKind defer_kind = defer_node->data.defer.kind;
results[defer_kind] += 1;
scope = scope->parent;
continue;
}
case ScopeIdDecls:
case ScopeIdFnDef:
return;
case ScopeIdBlock:
case ScopeIdVarDecl:
case ScopeIdLoop:
case ScopeIdSuspend:
case ScopeIdCompTime:
scope = scope->parent;
continue;
case ScopeIdDeferExpr:
case ScopeIdCImport:
case ScopeIdCoroPrelude:
zig_unreachable();
} }
inner_scope = inner_scope->parent;
} }
} }
@ -2984,27 +3004,43 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
if (!scope) if (!scope)
return is_noreturn; return is_noreturn;
if (scope->id == ScopeIdDefer) { switch (scope->id) {
AstNode *defer_node = scope->source_node; case ScopeIdDefer: {
assert(defer_node->type == NodeTypeDefer); AstNode *defer_node = scope->source_node;
ReturnKind defer_kind = defer_node->data.defer.kind; assert(defer_node->type == NodeTypeDefer);
if (defer_kind == ReturnKindUnconditional || ReturnKind defer_kind = defer_node->data.defer.kind;
(gen_error_defers && defer_kind == ReturnKindError)) if (defer_kind == ReturnKindUnconditional ||
{ (gen_error_defers && defer_kind == ReturnKindError))
AstNode *defer_expr_node = defer_node->data.defer.expr; {
Scope *defer_expr_scope = defer_node->data.defer.expr_scope; AstNode *defer_expr_node = defer_node->data.defer.expr;
IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
if (defer_expr_value != irb->codegen->invalid_instruction) { IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) { if (defer_expr_value != irb->codegen->invalid_instruction) {
is_noreturn = true; if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) {
} else { is_noreturn = true;
ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); } else {
ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));
}
} }
} }
scope = scope->parent;
continue;
} }
case ScopeIdDecls:
case ScopeIdFnDef:
return is_noreturn;
case ScopeIdBlock:
case ScopeIdVarDecl:
case ScopeIdLoop:
case ScopeIdSuspend:
case ScopeIdCompTime:
scope = scope->parent;
continue;
case ScopeIdDeferExpr:
case ScopeIdCImport:
case ScopeIdCoroPrelude:
zig_unreachable();
} }
scope = scope->parent;
} }
return is_noreturn; return is_noreturn;
} }
@ -9408,7 +9444,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc
if (type_is_invalid(casted_payload->value.type)) if (type_is_invalid(casted_payload->value.type))
return ira->codegen->invalid_instruction; return ira->codegen->invalid_instruction;
ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefBad); ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefOk);
if (!val) if (!val)
return ira->codegen->invalid_instruction; return ira->codegen->invalid_instruction;
@ -13090,6 +13126,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
impl_fn->ir_executable.parent_exec = ira->new_irb.exec; impl_fn->ir_executable.parent_exec = ira->new_irb.exec;
impl_fn->analyzed_executable.source_node = call_instruction->base.source_node; impl_fn->analyzed_executable.source_node = call_instruction->base.source_node;
impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec; impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec;
impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota;
impl_fn->analyzed_executable.is_generic_instantiation = true; impl_fn->analyzed_executable.is_generic_instantiation = true;
ira->codegen->fn_defs.append(impl_fn); ira->codegen->fn_defs.append(impl_fn);

View file

@ -901,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) {
if (strchr(buf_ptr(link_lib->name), '/') == nullptr) { if (strchr(buf_ptr(link_lib->name), '/') == nullptr) {
Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
lj->args.append(buf_ptr(arg)); lj->args.append(buf_ptr(arg));
} else { } else {
lj->args.append(buf_ptr(link_lib->name)); lj->args.append(buf_ptr(link_lib->name));
} }
} }

View file

@ -26,7 +26,6 @@
#include <windows.h> #include <windows.h>
#include <io.h> #include <io.h>
#include <fcntl.h> #include <fcntl.h>
#include "windows_com.hpp"
typedef SSIZE_T ssize_t; typedef SSIZE_T ssize_t;
#else #else
@ -1115,249 +1114,10 @@ void os_stderr_set_color(TermColor color) {
#endif #endif
} }
int os_find_windows_sdk(ZigWindowsSDK **out_sdk) {
#if defined(ZIG_OS_WINDOWS)
ZigWindowsSDK *result_sdk = allocate<ZigWindowsSDK>(1);
buf_resize(&result_sdk->path10, 0);
buf_resize(&result_sdk->path81, 0);
HKEY key;
HRESULT rc;
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key);
if (rc != ERROR_SUCCESS) {
return ErrorFileNotFound;
}
{
DWORD tmp_buf_len = MAX_PATH;
buf_resize(&result_sdk->path10, tmp_buf_len);
rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path10), &tmp_buf_len);
if (rc == ERROR_FILE_NOT_FOUND) {
buf_resize(&result_sdk->path10, 0);
} else {
buf_resize(&result_sdk->path10, tmp_buf_len);
}
}
{
DWORD tmp_buf_len = MAX_PATH;
buf_resize(&result_sdk->path81, tmp_buf_len);
rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path81), &tmp_buf_len);
if (rc == ERROR_FILE_NOT_FOUND) {
buf_resize(&result_sdk->path81, 0);
} else {
buf_resize(&result_sdk->path81, tmp_buf_len);
}
}
if (buf_len(&result_sdk->path10) != 0) {
Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\*", buf_ptr(&result_sdk->path10));
// enumerate files in sdk path looking for latest version
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return ErrorFileNotFound;
}
int v0 = 0, v1 = 0, v2 = 0, v3 = 0;
bool found_version_dir = false;
for (;;) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3);
if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) {
// Microsoft released 26624 as 10240 accidentally.
// https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
c2 = 26624;
}
if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
v0 = c0, v1 = c1, v2 = c2, v3 = c3;
buf_init_from_str(&result_sdk->version10, ffd.cFileName);
found_version_dir = true;
}
}
if (FindNextFile(hFind, &ffd) == 0) {
FindClose(hFind);
break;
}
}
if (!found_version_dir) {
buf_resize(&result_sdk->path10, 0);
}
}
if (buf_len(&result_sdk->path81) != 0) {
Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\winv*", buf_ptr(&result_sdk->path81));
// enumerate files in sdk path looking for latest version
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return ErrorFileNotFound;
}
int v0 = 0, v1 = 0;
bool found_version_dir = false;
for (;;) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
int c0 = 0, c1 = 0;
sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
if ((c0 > v0) || (c1 > v1)) {
v0 = c0, v1 = c1;
buf_init_from_str(&result_sdk->version81, ffd.cFileName);
found_version_dir = true;
}
}
if (FindNextFile(hFind, &ffd) == 0) {
FindClose(hFind);
break;
}
}
if (!found_version_dir) {
buf_resize(&result_sdk->path81, 0);
}
}
*out_sdk = result_sdk;
return 0;
#else
return ErrorFileNotFound;
#endif
}
int os_get_win32_vcruntime_path(Buf* output_buf, ZigLLVM_ArchType platform_type) {
#if defined(ZIG_OS_WINDOWS)
buf_resize(output_buf, 0);
//COM Smart Pointerse requires explicit scope
{
HRESULT rc;
rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (rc != S_OK) {
goto com_done;
}
//This COM class is installed when a VS2017
ISetupConfigurationPtr setup_config;
rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
if (rc != S_OK) {
goto com_done;
}
IEnumSetupInstancesPtr all_instances;
rc = setup_config->EnumInstances(&all_instances);
if (rc != S_OK) {
goto com_done;
}
ISetupInstance* curr_instance;
ULONG found_inst;
while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
BSTR bstr_inst_path;
rc = curr_instance->GetInstallationPath(&bstr_inst_path);
if (rc != S_OK) {
goto com_done;
}
//BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
ULONG tmp_path_len = bstr_path_len / 2 + 1;
char* conv_path = (char*)bstr_inst_path;
char *tmp_path = (char*)alloca(tmp_path_len);
memset(tmp_path, 0, tmp_path_len);
uint32_t c = 0;
for (uint32_t i = 0; i < bstr_path_len; i += 2) {
tmp_path[c] = conv_path[i];
++c;
assert(c != tmp_path_len);
}
buf_append_str(output_buf, tmp_path);
buf_append_char(output_buf, '\\');
Buf* tmp_buf = buf_alloc();
buf_append_buf(tmp_buf, output_buf);
buf_append_str(tmp_buf, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
FILE* tools_file = fopen(buf_ptr(tmp_buf), "r");
if (!tools_file) {
goto com_done;
}
memset(tmp_path, 0, tmp_path_len);
fgets(tmp_path, tmp_path_len, tools_file);
strtok(tmp_path, " \r\n");
fclose(tools_file);
buf_appendf(output_buf, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
switch (platform_type) {
case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\");
break;
case ZigLLVM_x86_64:
buf_append_str(output_buf, "x64\\");
break;
case ZigLLVM_arm:
buf_append_str(output_buf, "arm\\");
break;
default:
zig_panic("Attemped to use vcruntime for non-supported platform.");
}
buf_resize(tmp_buf, 0);
buf_append_buf(tmp_buf, output_buf);
buf_append_str(tmp_buf, "vcruntime.lib");
if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
return 0;
}
}
}
com_done:;
HKEY key;
HRESULT rc;
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
if (rc != ERROR_SUCCESS) {
return ErrorFileNotFound;
}
DWORD dw_type = 0;
DWORD cb_data = 0;
rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
return ErrorFileNotFound;
}
Buf* tmp_buf = buf_alloc_fixed(cb_data);
RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)buf_ptr(tmp_buf), &cb_data);
//RegQueryValueExA returns the length of the string INCLUDING the null terminator
buf_resize(tmp_buf, cb_data-1);
buf_append_str(tmp_buf, "VC\\Lib\\");
switch (platform_type) {
case ZigLLVM_x86:
//x86 is in the root of the Lib folder
break;
case ZigLLVM_x86_64:
buf_append_str(tmp_buf, "amd64\\");
break;
case ZigLLVM_arm:
buf_append_str(tmp_buf, "arm\\");
break;
default:
zig_panic("Attemped to use vcruntime for non-supported platform.");
}
buf_append_buf(output_buf, tmp_buf);
buf_append_str(tmp_buf, "vcruntime.lib");
if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
return 0;
} else {
buf_resize(output_buf, 0);
return ErrorFileNotFound;
}
#else
return ErrorFileNotFound;
#endif
}
int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
#if defined(ZIG_OS_WINDOWS) #if defined(ZIG_OS_WINDOWS)
buf_resize(output_buf, 0); buf_resize(output_buf, 0);
buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr);
switch (platform_type) { switch (platform_type) {
case ZigLLVM_x86: case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\"); buf_append_str(output_buf, "x86\\");
@ -1389,7 +1149,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch
int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) { int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) {
#if defined(ZIG_OS_WINDOWS) #if defined(ZIG_OS_WINDOWS)
buf_resize(output_buf, 0); buf_resize(output_buf, 0);
buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr);
if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) { if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) {
return 0; return 0;
} }
@ -1406,7 +1166,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
#if defined(ZIG_OS_WINDOWS) #if defined(ZIG_OS_WINDOWS)
{ {
buf_resize(output_buf, 0); buf_resize(output_buf, 0);
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr);
switch (platform_type) { switch (platform_type) {
case ZigLLVM_x86: case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\"); buf_append_str(output_buf, "x86\\");
@ -1429,7 +1189,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
} }
{ {
buf_resize(output_buf, 0); buf_resize(output_buf, 0);
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path81), buf_ptr(&sdk->version81)); buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr);
switch (platform_type) { switch (platform_type) {
case ZigLLVM_x86: case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\"); buf_append_str(output_buf, "x86\\");

View file

@ -12,6 +12,7 @@
#include "buffer.hpp" #include "buffer.hpp"
#include "error.hpp" #include "error.hpp"
#include "zig_llvm.h" #include "zig_llvm.h"
#include "windows_sdk.h"
#include <stdio.h> #include <stdio.h>
#include <inttypes.h> #include <inttypes.h>
@ -79,15 +80,6 @@ bool os_is_sep(uint8_t c);
int os_self_exe_path(Buf *out_path); int os_self_exe_path(Buf *out_path);
struct ZigWindowsSDK {
Buf path10;
Buf version10;
Buf path81;
Buf version81;
};
int os_find_windows_sdk(ZigWindowsSDK **out_sdk);
int os_get_win32_vcruntime_path(Buf *output_buf, ZigLLVM_ArchType platform_type);
int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf); int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf);
int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);

View file

@ -460,16 +460,21 @@ static const char* get_escape_shorthand(uint8_t c) {
static void invalid_char_error(Tokenize *t, uint8_t c) { static void invalid_char_error(Tokenize *t, uint8_t c) {
if (c == '\r') { if (c == '\r') {
tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported"); tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported");
} else if (isprint(c)) { return;
tokenize_error(t, "invalid character: '%c'", c);
} else {
const char *sh = get_escape_shorthand(c);
if (sh) {
tokenize_error(t, "invalid character: '%s'", sh);
} else {
tokenize_error(t, "invalid character: '\\x%x'", c);
}
} }
const char *sh = get_escape_shorthand(c);
if (sh) {
tokenize_error(t, "invalid character: '%s'", sh);
return;
}
if (isprint(c)) {
tokenize_error(t, "invalid character: '%c'", c);
return;
}
tokenize_error(t, "invalid character: '\\x%02x'", c);
} }
void tokenize(Buf *buf, Tokenization *out) { void tokenize(Buf *buf, Tokenization *out) {

352
src/windows_sdk.cpp Normal file
View file

@ -0,0 +1,352 @@
/*
* Copyright (c) 2018 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "windows_sdk.h"
#if defined(_WIN32)
#include "windows_com.hpp"
#include <inttypes.h>
#include <assert.h>
struct ZigWindowsSDKPrivate {
ZigWindowsSDK base;
};
enum NativeArch {
NativeArchArm,
NativeArchi386,
NativeArchx86_64,
};
#if defined(_M_ARM) || defined(__arm_)
static const NativeArch native_arch = NativeArchArm;
#endif
#if defined(_M_IX86) || defined(__i386__)
static const NativeArch native_arch = NativeArchi386;
#endif
#if defined(_M_X64) || defined(__x86_64__)
static const NativeArch native_arch = NativeArchx86_64;
#endif
void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {
if (sdk == nullptr) {
return;
}
free((void*)sdk->path10_ptr);
free((void*)sdk->version10_ptr);
free((void*)sdk->path81_ptr);
free((void*)sdk->version81_ptr);
free((void*)sdk->msvc_lib_dir_ptr);
}
static ZigFindWindowsSdkError find_msvc_lib_dir(ZigWindowsSDKPrivate *priv) {
//COM Smart Pointers requires explicit scope
{
HRESULT rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (rc != S_OK && rc != S_FALSE) {
goto com_done;
}
//This COM class is installed when a VS2017
ISetupConfigurationPtr setup_config;
rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
if (rc != S_OK) {
goto com_done;
}
IEnumSetupInstancesPtr all_instances;
rc = setup_config->EnumInstances(&all_instances);
if (rc != S_OK) {
goto com_done;
}
ISetupInstance* curr_instance;
ULONG found_inst;
while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
BSTR bstr_inst_path;
rc = curr_instance->GetInstallationPath(&bstr_inst_path);
if (rc != S_OK) {
goto com_done;
}
//BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
//TODO call an actual function to do this
UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
ULONG tmp_path_len = bstr_path_len / 2 + 1;
char* conv_path = (char*)bstr_inst_path;
// TODO don't use alloca
char *tmp_path = (char*)alloca(tmp_path_len);
memset(tmp_path, 0, tmp_path_len);
uint32_t c = 0;
for (uint32_t i = 0; i < bstr_path_len; i += 2) {
tmp_path[c] = conv_path[i];
++c;
assert(c != tmp_path_len);
}
char output_path[4096];
output_path[0] = 0;
char *out_append_ptr = output_path;
out_append_ptr += sprintf(out_append_ptr, "%s\\", tmp_path);
char tmp_buf[4096];
sprintf(tmp_buf, "%s%s", output_path, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
FILE* tools_file = fopen(tmp_buf, "rb");
if (!tools_file) {
goto com_done;
}
memset(tmp_path, 0, tmp_path_len);
fgets(tmp_path, tmp_path_len, tools_file);
strtok(tmp_path, " \r\n");
fclose(tools_file);
out_append_ptr += sprintf(out_append_ptr, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
switch (native_arch) {
case NativeArchi386:
out_append_ptr += sprintf(out_append_ptr, "x86\\");
break;
case NativeArchx86_64:
out_append_ptr += sprintf(out_append_ptr, "x64\\");
break;
case NativeArchArm:
out_append_ptr += sprintf(out_append_ptr, "arm\\");
break;
}
sprintf(tmp_buf, "%s%s", output_path, "vcruntime.lib");
if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
priv->base.msvc_lib_dir_ptr = strdup(output_path);
if (priv->base.msvc_lib_dir_ptr == nullptr) {
return ZigFindWindowsSdkErrorOutOfMemory;
}
priv->base.msvc_lib_dir_len = strlen(priv->base.msvc_lib_dir_ptr);
return ZigFindWindowsSdkErrorNone;
}
}
}
com_done:;
HKEY key;
HRESULT rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0,
KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
if (rc != ERROR_SUCCESS) {
return ZigFindWindowsSdkErrorNotFound;
}
DWORD dw_type = 0;
DWORD cb_data = 0;
rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
return ZigFindWindowsSdkErrorNotFound;
}
char tmp_buf[4096];
RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)tmp_buf, &cb_data);
// RegQueryValueExA returns the length of the string INCLUDING the null terminator
char *tmp_buf_append_ptr = tmp_buf + (cb_data - 1);
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "VC\\Lib\\");
switch (native_arch) {
case NativeArchi386:
//x86 is in the root of the Lib folder
break;
case NativeArchx86_64:
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "amd64\\");
break;
case NativeArchArm:
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm\\");
break;
}
char *output_path = strdup(tmp_buf);
if (output_path == nullptr) {
return ZigFindWindowsSdkErrorOutOfMemory;
}
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "vcruntime.lib");
if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
priv->base.msvc_lib_dir_ptr = output_path;
priv->base.msvc_lib_dir_len = strlen(output_path);
return ZigFindWindowsSdkErrorNone;
} else {
free(output_path);
return ZigFindWindowsSdkErrorNotFound;
}
}
static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) {
if (priv->base.path10_ptr == nullptr)
return ZigFindWindowsSdkErrorNone;
char sdk_lib_dir[4096];
int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\*", priv->base.path10_ptr);
if (n < 0 || n >= 4096) {
return ZigFindWindowsSdkErrorPathTooLong;
}
// enumerate files in sdk path looking for latest version
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return ZigFindWindowsSdkErrorNotFound;
}
int v0 = 0, v1 = 0, v2 = 0, v3 = 0;
for (;;) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3);
if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) {
// Microsoft released 26624 as 10240 accidentally.
// https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
c2 = 26624;
}
if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
v0 = c0, v1 = c1, v2 = c2, v3 = c3;
free((void*)priv->base.version10_ptr);
priv->base.version10_ptr = strdup(ffd.cFileName);
if (priv->base.version10_ptr == nullptr) {
FindClose(hFind);
return ZigFindWindowsSdkErrorOutOfMemory;
}
}
}
if (FindNextFile(hFind, &ffd) == 0) {
FindClose(hFind);
break;
}
}
priv->base.version10_len = strlen(priv->base.version10_ptr);
return ZigFindWindowsSdkErrorNone;
}
static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) {
if (priv->base.path81_ptr == nullptr)
return ZigFindWindowsSdkErrorNone;
char sdk_lib_dir[4096];
int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\winv*", priv->base.path81_ptr);
if (n < 0 || n >= 4096) {
return ZigFindWindowsSdkErrorPathTooLong;
}
// enumerate files in sdk path looking for latest version
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return ZigFindWindowsSdkErrorNotFound;
}
int v0 = 0, v1 = 0;
for (;;) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
int c0 = 0, c1 = 0;
sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
if ((c0 > v0) || (c1 > v1)) {
v0 = c0, v1 = c1;
free((void*)priv->base.version81_ptr);
priv->base.version81_ptr = strdup(ffd.cFileName);
if (priv->base.version81_ptr == nullptr) {
FindClose(hFind);
return ZigFindWindowsSdkErrorOutOfMemory;
}
}
}
if (FindNextFile(hFind, &ffd) == 0) {
FindClose(hFind);
break;
}
}
priv->base.version81_len = strlen(priv->base.version81_ptr);
return ZigFindWindowsSdkErrorNone;
}
ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
ZigWindowsSDKPrivate *priv = (ZigWindowsSDKPrivate*)calloc(1, sizeof(ZigWindowsSDKPrivate));
if (priv == nullptr) {
return ZigFindWindowsSdkErrorOutOfMemory;
}
HKEY key;
HRESULT rc;
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0,
KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key);
if (rc != ERROR_SUCCESS) {
zig_free_windows_sdk(&priv->base);
return ZigFindWindowsSdkErrorNotFound;
}
{
DWORD tmp_buf_len = MAX_PATH;
priv->base.path10_ptr = (const char *)calloc(tmp_buf_len, 1);
if (priv->base.path10_ptr == nullptr) {
zig_free_windows_sdk(&priv->base);
return ZigFindWindowsSdkErrorOutOfMemory;
}
rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len);
if (rc == ERROR_SUCCESS) {
priv->base.path10_len = tmp_buf_len - 1;
if (priv->base.path10_ptr[priv->base.path10_len - 1] == '\\') {
priv->base.path10_len -= 1;
}
} else {
free((void*)priv->base.path10_ptr);
priv->base.path10_ptr = nullptr;
}
}
{
DWORD tmp_buf_len = MAX_PATH;
priv->base.path81_ptr = (const char *)calloc(tmp_buf_len, 1);
if (priv->base.path81_ptr == nullptr) {
zig_free_windows_sdk(&priv->base);
return ZigFindWindowsSdkErrorOutOfMemory;
}
rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len);
if (rc == ERROR_SUCCESS) {
priv->base.path81_len = tmp_buf_len - 1;
if (priv->base.path81_ptr[priv->base.path81_len - 1] == '\\') {
priv->base.path81_len -= 1;
}
} else {
free((void*)priv->base.path81_ptr);
priv->base.path81_ptr = nullptr;
}
}
{
ZigFindWindowsSdkError err = find_10_version(priv);
if (err == ZigFindWindowsSdkErrorOutOfMemory) {
zig_free_windows_sdk(&priv->base);
return err;
}
}
{
ZigFindWindowsSdkError err = find_81_version(priv);
if (err == ZigFindWindowsSdkErrorOutOfMemory) {
zig_free_windows_sdk(&priv->base);
return err;
}
}
{
ZigFindWindowsSdkError err = find_msvc_lib_dir(priv);
if (err == ZigFindWindowsSdkErrorOutOfMemory) {
zig_free_windows_sdk(&priv->base);
return err;
}
}
*out_sdk = &priv->base;
return ZigFindWindowsSdkErrorNone;
}
#else
void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {}
ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
return ZigFindWindowsSdkErrorNotFound;
}
#endif

47
src/windows_sdk.h Normal file
View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2018 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef ZIG_WINDOWS_SDK_H
#define ZIG_WINDOWS_SDK_H
#ifdef __cplusplus
#define ZIG_EXTERN_C extern "C"
#else
#define ZIG_EXTERN_C
#endif
#include <stddef.h>
struct ZigWindowsSDK {
const char *path10_ptr;
size_t path10_len;
const char *version10_ptr;
size_t version10_len;
const char *path81_ptr;
size_t path81_len;
const char *version81_ptr;
size_t version81_len;
const char *msvc_lib_dir_ptr;
size_t msvc_lib_dir_len;
};
enum ZigFindWindowsSdkError {
ZigFindWindowsSdkErrorNone,
ZigFindWindowsSdkErrorOutOfMemory,
ZigFindWindowsSdkErrorNotFound,
ZigFindWindowsSdkErrorPathTooLong,
};
ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk);
ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk);
#endif

View file

@ -455,6 +455,11 @@ ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unreso
return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder); return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder);
} }
void ZigLLVMDisposeDIBuilder(ZigLLVMDIBuilder *dbuilder) {
DIBuilder *di_builder = reinterpret_cast<DIBuilder *>(dbuilder);
delete di_builder;
}
void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) { void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) {
unwrap(builder)->SetCurrentDebugLocation(DebugLoc::get( unwrap(builder)->SetCurrentDebugLocation(DebugLoc::get(
line, column, reinterpret_cast<DIScope*>(scope))); line, column, reinterpret_cast<DIScope*>(scope)));

View file

@ -22,6 +22,9 @@
#define ZIG_EXTERN_C #define ZIG_EXTERN_C
#endif #endif
// ATTENTION: If you modify this file, be sure to update the corresponding
// extern function declarations in the self-hosted compiler.
struct ZigLLVMDIType; struct ZigLLVMDIType;
struct ZigLLVMDIBuilder; struct ZigLLVMDIBuilder;
struct ZigLLVMDICompileUnit; struct ZigLLVMDICompileUnit;
@ -39,7 +42,7 @@ struct ZigLLVMInsertionPoint;
ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R); ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R);
ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R); ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R);
/// Caller must free memory. /// Caller must free memory with LLVMDisposeMessage
ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void); ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void);
ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void); ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
@ -145,6 +148,7 @@ ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void);
ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void); ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void);
ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved); ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);
ZIG_EXTERN_C void ZigLLVMDisposeDIBuilder(struct ZigLLVMDIBuilder *dbuilder);
ZIG_EXTERN_C void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module); ZIG_EXTERN_C void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module);
ZIG_EXTERN_C void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module); ZIG_EXTERN_C void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module);

View file

@ -113,13 +113,12 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
return old_item; return old_item;
} }
pub fn removeOrError(self: *Self, n: usize) !T { /// Removes the element at the specified index and returns it
if (n >= self.len) return error.OutOfBounds; /// or an error.OutOfBounds is returned. If no error then
if (self.len - 1 == n) return self.pop(); /// the empty slot is filled from the end of the list.
pub fn swapRemoveOrError(self: *Self, i: usize) !T {
var old_item = self.at(n); if (i >= self.len) return error.OutOfBounds;
try self.setOrError(n, self.pop()); return self.swapRemove(i);
return old_item;
} }
pub fn appendSlice(self: *Self, items: []align(A) const T) !void { pub fn appendSlice(self: *Self, items: []align(A) const T) !void {
@ -192,7 +191,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
}; };
} }
test "basic ArrayList test" { test "std.ArrayList.basic" {
var bytes: [1024]u8 = undefined; var bytes: [1024]u8 = undefined;
const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
@ -279,7 +278,35 @@ test "std.ArrayList.swapRemove" {
assert(list.len == 4); assert(list.len == 4);
} }
test "iterator ArrayList test" { test "std.ArrayList.swapRemoveOrError" {
var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit();
// Test just after initialization
assertError(list.swapRemoveOrError(0), error.OutOfBounds);
// Test after adding one item and remote it
try list.append(1);
assert((try list.swapRemoveOrError(0)) == 1);
assertError(list.swapRemoveOrError(0), error.OutOfBounds);
// Test after adding two items and remote both
try list.append(1);
try list.append(2);
assert((try list.swapRemoveOrError(1)) == 2);
assert((try list.swapRemoveOrError(0)) == 1);
assertError(list.swapRemoveOrError(0), error.OutOfBounds);
// Test out of bounds with one item
try list.append(1);
assertError(list.swapRemoveOrError(1), error.OutOfBounds);
// Test out of bounds with two items
try list.append(2);
assertError(list.swapRemoveOrError(2), error.OutOfBounds);
}
test "std.ArrayList.iterator" {
var list = ArrayList(i32).init(debug.global_allocator); var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit(); defer list.deinit();
@ -308,7 +335,7 @@ test "iterator ArrayList test" {
assert(it.next().? == 1); assert(it.next().? == 1);
} }
test "insert ArrayList test" { test "std.ArrayList.insert" {
var list = ArrayList(i32).init(debug.global_allocator); var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit(); defer list.deinit();
@ -322,7 +349,7 @@ test "insert ArrayList test" {
assert(list.items[3] == 3); assert(list.items[3] == 3);
} }
test "insertSlice ArrayList test" { test "std.ArrayList.insertSlice" {
var list = ArrayList(i32).init(debug.global_allocator); var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit(); defer list.deinit();

View file

@ -25,5 +25,9 @@ pub fn Int(comptime T: type) type {
pub fn get(self: *Self) T { pub fn get(self: *Self) T {
return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst); return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst);
} }
pub fn xchg(self: *Self, new_value: T) T {
return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Xchg, new_value, AtomicOrder.SeqCst);
}
}; };
} }

View file

@ -51,6 +51,20 @@ pub fn Queue(comptime T: type) type {
return head; return head;
} }
pub fn unget(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);
const opt_head = self.head;
self.head = node;
if (opt_head) |head| {
head.next = node;
} else {
assert(self.tail == null);
self.tail = node;
}
}
pub fn isEmpty(self: *Self) bool { pub fn isEmpty(self: *Self) bool {
return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null; return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null;
} }

View file

@ -54,6 +54,19 @@ pub const Buffer = struct {
return result; return result;
} }
pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: ...) !Buffer {
const countSize = struct {
fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
size.* += bytes.len;
}
}.countSize;
var size: usize = 0;
std.fmt.format(&size, error{}, countSize, format, args) catch |err| switch (err) {};
var self = try Buffer.initSize(allocator, size);
assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size);
return self;
}
pub fn deinit(self: *Buffer) void { pub fn deinit(self: *Buffer) void {
self.list.deinit(); self.list.deinit();
} }

View file

@ -30,7 +30,7 @@ pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlen
pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int;
pub use @import("../os/darwin_errno.zig"); pub use @import("../os/darwin/errno.zig");
pub const _errno = __error; pub const _errno = __error;

View file

@ -639,3 +639,40 @@ pub const LNE_define_file = 0x03;
pub const LNE_set_discriminator = 0x04; pub const LNE_set_discriminator = 0x04;
pub const LNE_lo_user = 0x80; pub const LNE_lo_user = 0x80;
pub const LNE_hi_user = 0xff; pub const LNE_hi_user = 0xff;
pub const LANG_C89 = 0x0001;
pub const LANG_C = 0x0002;
pub const LANG_Ada83 = 0x0003;
pub const LANG_C_plus_plus = 0x0004;
pub const LANG_Cobol74 = 0x0005;
pub const LANG_Cobol85 = 0x0006;
pub const LANG_Fortran77 = 0x0007;
pub const LANG_Fortran90 = 0x0008;
pub const LANG_Pascal83 = 0x0009;
pub const LANG_Modula2 = 0x000a;
pub const LANG_Java = 0x000b;
pub const LANG_C99 = 0x000c;
pub const LANG_Ada95 = 0x000d;
pub const LANG_Fortran95 = 0x000e;
pub const LANG_PLI = 0x000f;
pub const LANG_ObjC = 0x0010;
pub const LANG_ObjC_plus_plus = 0x0011;
pub const LANG_UPC = 0x0012;
pub const LANG_D = 0x0013;
pub const LANG_Python = 0x0014;
pub const LANG_Go = 0x0016;
pub const LANG_C_plus_plus_11 = 0x001a;
pub const LANG_Rust = 0x001c;
pub const LANG_C11 = 0x001d;
pub const LANG_C_plus_plus_14 = 0x0021;
pub const LANG_Fortran03 = 0x0022;
pub const LANG_Fortran08 = 0x0023;
pub const LANG_lo_user = 0x8000;
pub const LANG_hi_user = 0xffff;
pub const LANG_Mips_Assembler = 0x8001;
pub const LANG_Upc = 0x8765;
pub const LANG_HP_Bliss = 0x8003;
pub const LANG_HP_Basic91 = 0x8004;
pub const LANG_HP_Pascal91 = 0x8005;
pub const LANG_HP_IMacro = 0x8006;
pub const LANG_HP_Assembler = 0x8007;

View file

@ -6,15 +6,20 @@ const AtomicOrder = builtin.AtomicOrder;
const Lock = std.event.Lock; const Lock = std.event.Lock;
const Loop = std.event.Loop; const Loop = std.event.Loop;
/// This is a value that starts out unavailable, until a value is put(). /// This is a value that starts out unavailable, until resolve() is called
/// While it is unavailable, coroutines suspend when they try to get() it, /// While it is unavailable, coroutines suspend when they try to get() it,
/// and then are resumed when the value is put(). /// and then are resumed when resolve() is called.
/// At this point the value remains forever available, and another put() is not allowed. /// At this point the value remains forever available, and another resolve() is not allowed.
pub fn Future(comptime T: type) type { pub fn Future(comptime T: type) type {
return struct { return struct {
lock: Lock, lock: Lock,
data: T, data: T,
available: u8, // TODO make this a bool
/// TODO make this an enum
/// 0 - not started
/// 1 - started
/// 2 - finished
available: u8,
const Self = this; const Self = this;
const Queue = std.atomic.Queue(promise); const Queue = std.atomic.Queue(promise);
@ -31,7 +36,7 @@ pub fn Future(comptime T: type) type {
/// available. /// available.
/// Thread-safe. /// Thread-safe.
pub async fn get(self: *Self) *T { pub async fn get(self: *Self) *T {
if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {
return &self.data; return &self.data;
} }
const held = await (async self.lock.acquire() catch unreachable); const held = await (async self.lock.acquire() catch unreachable);
@ -43,18 +48,36 @@ pub fn Future(comptime T: type) type {
/// Gets the data without waiting for it. If it's available, a pointer is /// Gets the data without waiting for it. If it's available, a pointer is
/// returned. Otherwise, null is returned. /// returned. Otherwise, null is returned.
pub fn getOrNull(self: *Self) ?*T { pub fn getOrNull(self: *Self) ?*T {
if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {
return &self.data; return &self.data;
} else { } else {
return null; return null;
} }
} }
/// If someone else has started working on the data, wait for them to complete
/// and return a pointer to the data. Otherwise, return null, and the caller
/// should start working on the data.
/// It's not required to call start() before resolve() but it can be useful since
/// this method is thread-safe.
pub async fn start(self: *Self) ?*T {
const state = @cmpxchgStrong(u8, &self.available, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null;
switch (state) {
1 => {
const held = await (async self.lock.acquire() catch unreachable);
held.release();
return &self.data;
},
2 => return &self.data,
else => unreachable,
}
}
/// Make the data become available. May be called only once. /// Make the data become available. May be called only once.
/// Before calling this, modify the `data` property. /// Before calling this, modify the `data` property.
pub fn resolve(self: *Self) void { pub fn resolve(self: *Self) void {
const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst);
assert(prev == 0); // put() called twice assert(prev == 0 or prev == 1); // resolve() called twice
Lock.Held.release(Lock.Held{ .lock = &self.lock }); Lock.Held.release(Lock.Held{ .lock = &self.lock });
} }
}; };

View file

@ -6,7 +6,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
const AtomicOrder = builtin.AtomicOrder; const AtomicOrder = builtin.AtomicOrder;
const assert = std.debug.assert; const assert = std.debug.assert;
/// ReturnType should be `void` or `E!void` /// ReturnType must be `void` or `E!void`
pub fn Group(comptime ReturnType: type) type { pub fn Group(comptime ReturnType: type) type {
return struct { return struct {
coro_stack: Stack, coro_stack: Stack,
@ -38,8 +38,17 @@ pub fn Group(comptime ReturnType: type) type {
self.alloc_stack.push(node); self.alloc_stack.push(node);
} }
/// Add a node to the group. Thread-safe. Cannot fail.
/// `node.data` should be the promise handle to add to the group.
/// The node's memory should be in the coroutine frame of
/// the handle that is in the node, or somewhere guaranteed to live
/// at least as long.
pub fn addNode(self: *Self, node: *Stack.Node) void {
self.coro_stack.push(node);
}
/// This is equivalent to an async call, but the async function is added to the group, instead /// This is equivalent to an async call, but the async function is added to the group, instead
/// of returning a promise. func must be async and have return type void. /// of returning a promise. func must be async and have return type ReturnType.
/// Thread-safe. /// Thread-safe.
pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) { pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) {
const S = struct { const S = struct {
@ -67,6 +76,7 @@ pub fn Group(comptime ReturnType: type) type {
/// Wait for all the calls and promises of the group to complete. /// Wait for all the calls and promises of the group to complete.
/// Thread-safe. /// Thread-safe.
/// Safe to call any number of times.
pub async fn wait(self: *Self) ReturnType { pub async fn wait(self: *Self) ReturnType {
// TODO catch unreachable because the allocation can be grouped with // TODO catch unreachable because the allocation can be grouped with
// the coro frame allocation // the coro frame allocation
@ -98,6 +108,8 @@ pub fn Group(comptime ReturnType: type) type {
} }
/// Cancel all the outstanding promises. May only be called if wait was never called. /// Cancel all the outstanding promises. May only be called if wait was never called.
/// TODO These should be `cancelasync` not `cancel`.
/// See https://github.com/ziglang/zig/issues/1261
pub fn cancelAll(self: *Self) void { pub fn cancelAll(self: *Self) void {
while (self.coro_stack.pop()) |node| { while (self.coro_stack.pop()) |node| {
cancel node.data; cancel node.data;

View file

@ -12,7 +12,6 @@ pub const Loop = struct {
next_tick_queue: std.atomic.Queue(promise), next_tick_queue: std.atomic.Queue(promise),
os_data: OsData, os_data: OsData,
final_resume_node: ResumeNode, final_resume_node: ResumeNode,
dispatch_lock: u8, // TODO make this a bool
pending_event_count: usize, pending_event_count: usize,
extra_threads: []*std.os.Thread, extra_threads: []*std.os.Thread,
@ -74,11 +73,10 @@ pub const Loop = struct {
/// max(thread_count - 1, 0) /// max(thread_count - 1, 0)
fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void { fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void {
self.* = Loop{ self.* = Loop{
.pending_event_count = 0, .pending_event_count = 1,
.allocator = allocator, .allocator = allocator,
.os_data = undefined, .os_data = undefined,
.next_tick_queue = std.atomic.Queue(promise).init(), .next_tick_queue = std.atomic.Queue(promise).init(),
.dispatch_lock = 1, // start locked so threads go directly into epoll wait
.extra_threads = undefined, .extra_threads = undefined,
.available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(), .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(),
.eventfd_resume_nodes = undefined, .eventfd_resume_nodes = undefined,
@ -235,8 +233,6 @@ pub const Loop = struct {
} }
}, },
builtin.Os.windows => { builtin.Os.windows => {
self.os_data.extra_thread_count = extra_thread_count;
self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( self.os_data.io_port = try std.os.windowsCreateIoCompletionPort(
windows.INVALID_HANDLE_VALUE, windows.INVALID_HANDLE_VALUE,
null, null,
@ -306,7 +302,7 @@ pub const Loop = struct {
pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void { pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void {
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
errdefer { errdefer {
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); self.finishOneEvent();
} }
try self.modFd( try self.modFd(
fd, fd,
@ -326,7 +322,7 @@ pub const Loop = struct {
pub fn removeFd(self: *Loop, fd: i32) void { pub fn removeFd(self: *Loop, fd: i32) void {
self.removeFdNoCounter(fd); self.removeFdNoCounter(fd);
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); self.finishOneEvent();
} }
fn removeFdNoCounter(self: *Loop, fd: i32) void { fn removeFdNoCounter(self: *Loop, fd: i32) void {
@ -345,14 +341,70 @@ pub const Loop = struct {
} }
} }
fn dispatch(self: *Loop) void {
while (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| {
const next_tick_node = self.next_tick_queue.get() orelse {
self.available_eventfd_resume_nodes.push(resume_stack_node);
return;
};
const eventfd_node = &resume_stack_node.data;
eventfd_node.base.handle = next_tick_node.data;
switch (builtin.os) {
builtin.Os.macosx => {
const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);
const eventlist = ([*]posix.Kevent)(undefined)[0..0];
_ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {
self.next_tick_queue.unget(next_tick_node);
self.available_eventfd_resume_nodes.push(resume_stack_node);
return;
};
},
builtin.Os.linux => {
// the pending count is already accounted for
const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT |
std.os.linux.EPOLLET;
self.modFd(
eventfd_node.eventfd,
eventfd_node.epoll_op,
epoll_events,
&eventfd_node.base,
) catch {
self.next_tick_queue.unget(next_tick_node);
self.available_eventfd_resume_nodes.push(resume_stack_node);
return;
};
},
builtin.Os.windows => {
// this value is never dereferenced but we need it to be non-null so that
// the consumer code can decide whether to read the completion key.
// it has to do this for normal I/O, so we match that behavior here.
const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
std.os.windowsPostQueuedCompletionStatus(
self.os_data.io_port,
undefined,
eventfd_node.completion_key,
overlapped,
) catch {
self.next_tick_queue.unget(next_tick_node);
self.available_eventfd_resume_nodes.push(resume_stack_node);
return;
};
},
else => @compileError("unsupported OS"),
}
}
}
/// Bring your own linked list node. This means it can't fail. /// Bring your own linked list node. This means it can't fail.
pub fn onNextTick(self: *Loop, node: *NextTickNode) void { pub fn onNextTick(self: *Loop, node: *NextTickNode) void {
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
self.next_tick_queue.put(node); self.next_tick_queue.put(node);
self.dispatch();
} }
pub fn run(self: *Loop) void { pub fn run(self: *Loop) void {
_ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); self.finishOneEvent(); // the reference we start with
self.workerRun(); self.workerRun();
for (self.extra_threads) |extra_thread| { for (self.extra_threads) |extra_thread| {
extra_thread.wait(); extra_thread.wait();
@ -392,110 +444,49 @@ pub const Loop = struct {
.next = undefined, .next = undefined,
.data = p, .data = p,
}; };
loop.onNextTick(&my_tick_node); self.onNextTick(&my_tick_node);
}
}
fn finishOneEvent(self: *Loop) void {
if (@atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) == 1) {
// cause all the threads to stop
switch (builtin.os) {
builtin.Os.linux => {
// writing 8 bytes to an eventfd cannot fail
std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
return;
},
builtin.Os.macosx => {
const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);
const eventlist = ([*]posix.Kevent)(undefined)[0..0];
// cannot fail because we already added it and this just enables it
_ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable;
return;
},
builtin.Os.windows => {
var i: usize = 0;
while (i < self.extra_threads.len + 1) : (i += 1) {
while (true) {
const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
break;
}
}
return;
},
else => @compileError("unsupported OS"),
}
} }
} }
fn workerRun(self: *Loop) void { fn workerRun(self: *Loop) void {
start_over: while (true) { while (true) {
if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { while (true) {
while (self.next_tick_queue.get()) |next_tick_node| { const next_tick_node = self.next_tick_queue.get() orelse break;
const handle = next_tick_node.data; self.dispatch();
if (self.next_tick_queue.isEmpty()) { resume next_tick_node.data;
// last node, just resume it self.finishOneEvent();
_ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
resume handle;
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
continue :start_over;
}
// non-last node, stick it in the epoll/kqueue set so that
// other threads can get to it
if (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| {
const eventfd_node = &resume_stack_node.data;
eventfd_node.base.handle = handle;
switch (builtin.os) {
builtin.Os.macosx => {
const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);
const eventlist = ([*]posix.Kevent)(undefined)[0..0];
_ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {
// fine, we didn't need it anyway
_ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
self.available_eventfd_resume_nodes.push(resume_stack_node);
resume handle;
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
continue :start_over;
};
},
builtin.Os.linux => {
// the pending count is already accounted for
const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET;
self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch {
// fine, we didn't need it anyway
_ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
self.available_eventfd_resume_nodes.push(resume_stack_node);
resume handle;
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
continue :start_over;
};
},
builtin.Os.windows => {
// this value is never dereferenced but we need it to be non-null so that
// the consumer code can decide whether to read the completion key.
// it has to do this for normal I/O, so we match that behavior here.
const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, eventfd_node.completion_key, overlapped) catch {
// fine, we didn't need it anyway
_ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
self.available_eventfd_resume_nodes.push(resume_stack_node);
resume handle;
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
continue :start_over;
};
},
else => @compileError("unsupported OS"),
}
} else {
// threads are too busy, can't add another eventfd to wake one up
_ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
resume handle;
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
continue :start_over;
}
}
const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst);
if (pending_event_count == 0) {
// cause all the threads to stop
switch (builtin.os) {
builtin.Os.linux => {
// writing 8 bytes to an eventfd cannot fail
std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
return;
},
builtin.Os.macosx => {
const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);
const eventlist = ([*]posix.Kevent)(undefined)[0..0];
// cannot fail because we already added it and this just enables it
_ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable;
return;
},
builtin.Os.windows => {
var i: usize = 0;
while (i < self.os_data.extra_thread_count) : (i += 1) {
while (true) {
const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
break;
}
}
return;
},
else => @compileError("unsupported OS"),
}
}
_ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
} }
switch (builtin.os) { switch (builtin.os) {
@ -519,7 +510,7 @@ pub const Loop = struct {
} }
resume handle; resume handle;
if (resume_node_id == ResumeNode.Id.EventFd) { if (resume_node_id == ResumeNode.Id.EventFd) {
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); self.finishOneEvent();
} }
} }
}, },
@ -541,7 +532,7 @@ pub const Loop = struct {
} }
resume handle; resume handle;
if (resume_node_id == ResumeNode.Id.EventFd) { if (resume_node_id == ResumeNode.Id.EventFd) {
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); self.finishOneEvent();
} }
} }
}, },
@ -570,7 +561,7 @@ pub const Loop = struct {
} }
resume handle; resume handle;
if (resume_node_id == ResumeNode.Id.EventFd) { if (resume_node_id == ResumeNode.Id.EventFd) {
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); self.finishOneEvent();
} }
}, },
else => @compileError("unsupported OS"), else => @compileError("unsupported OS"),

View file

@ -125,8 +125,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File
test "listen on a port, send bytes, receive bytes" { test "listen on a port, send bytes, receive bytes" {
if (builtin.os != builtin.Os.linux) { if (builtin.os != builtin.Os.linux) {
// TODO build abstractions for other operating systems // TODO build abstractions for other operating systems
return; return error.SkipZigTest;
} }
const MyServer = struct { const MyServer = struct {
tcp_server: Server, tcp_server: Server,

View file

@ -785,11 +785,15 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 {
return buf[0 .. buf.len - context.remaining.len]; return buf[0 .. buf.len - context.remaining.len];
} }
pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { pub const AllocPrintError = error{OutOfMemory};
pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 {
var size: usize = 0; var size: usize = 0;
format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
const buf = try allocator.alloc(u8, size); const buf = try allocator.alloc(u8, size);
return bufPrint(buf, fmt, args); return bufPrint(buf, fmt, args) catch |err| switch (err) {
error.BufferTooSmall => unreachable, // we just counted the size above
};
} }
fn countSize(size: *usize, bytes: []const u8) (error{}!void) { fn countSize(size: *usize, bytes: []const u8) (error{}!void) {

View file

@ -36,6 +36,8 @@ pub const sort = @import("sort.zig");
pub const unicode = @import("unicode.zig"); pub const unicode = @import("unicode.zig");
pub const zig = @import("zig/index.zig"); pub const zig = @import("zig/index.zig");
pub const lazyInit = @import("lazy_init.zig").lazyInit;
test "std" { test "std" {
// run tests from these // run tests from these
_ = @import("atomic/index.zig"); _ = @import("atomic/index.zig");
@ -71,4 +73,5 @@ test "std" {
_ = @import("sort.zig"); _ = @import("sort.zig");
_ = @import("unicode.zig"); _ = @import("unicode.zig");
_ = @import("zig/index.zig"); _ = @import("zig/index.zig");
_ = @import("lazy_init.zig");
} }

85
std/lazy_init.zig Normal file
View file

@ -0,0 +1,85 @@
const std = @import("index.zig");
const builtin = @import("builtin");
const assert = std.debug.assert;
const AtomicRmwOp = builtin.AtomicRmwOp;
const AtomicOrder = builtin.AtomicOrder;
/// Thread-safe initialization of global data.
/// TODO use a mutex instead of a spinlock
pub fn lazyInit(comptime T: type) LazyInit(T) {
return LazyInit(T){
.data = undefined,
.state = 0,
};
}
fn LazyInit(comptime T: type) type {
return struct {
state: u8, // TODO make this an enum
data: Data,
const Self = this;
// TODO this isn't working for void, investigate and then remove this special case
const Data = if (@sizeOf(T) == 0) u8 else T;
const Ptr = if (T == void) void else *T;
/// Returns a usable pointer to the initialized data,
/// or returns null, indicating that the caller should
/// perform the initialization and then call resolve().
pub fn get(self: *Self) ?Ptr {
while (true) {
var state = @cmpxchgWeak(u8, &self.state, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null;
switch (state) {
0 => continue,
1 => {
// TODO mutex instead of a spinlock
continue;
},
2 => {
if (@sizeOf(T) == 0) {
return T(undefined);
} else {
return &self.data;
}
},
else => unreachable,
}
}
}
pub fn resolve(self: *Self) void {
const prev = @atomicRmw(u8, &self.state, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst);
assert(prev == 1); // resolve() called twice
}
};
}
var global_number = lazyInit(i32);
test "std.lazyInit" {
if (global_number.get()) |_| @panic("bad") else {
global_number.data = 1234;
global_number.resolve();
}
if (global_number.get()) |x| {
assert(x.* == 1234);
} else {
@panic("bad");
}
if (global_number.get()) |x| {
assert(x.* == 1234);
} else {
@panic("bad");
}
}
var global_void = lazyInit(void);
test "std.lazyInit(void)" {
if (global_void.get()) |_| @panic("bad") else {
global_void.resolve();
}
assert(global_void.get() != null);
assert(global_void.get() != null);
}

View file

@ -141,7 +141,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable
} }
// Effectively a no-op, lld emits symbols in ascending order. // Effectively a no-op, lld emits symbols in ascending order.
std.sort.insertionSort(Symbol, symbols[0..nsyms], Symbol.addressLessThan); std.sort.sort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);
// Insert the sentinel. Since we don't know where the last function ends, // Insert the sentinel. Since we don't know where the last function ends,
// we arbitrarily limit it to the start address + 4 KB. // we arbitrarily limit it to the start address + 4 KB.

View file

@ -60,8 +60,9 @@ pub const Int = struct {
self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity); self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity);
} }
pub fn deinit(self: Int) void { pub fn deinit(self: *Int) void {
self.allocator.free(self.limbs); self.allocator.free(self.limbs);
self.* = undefined;
} }
pub fn clone(other: Int) !Int { pub fn clone(other: Int) !Int {
@ -115,13 +116,63 @@ pub const Int = struct {
return !r.isOdd(); return !r.isOdd();
} }
fn bitcount(self: Int) usize { // Returns the number of bits required to represent the absolute value of self.
const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); fn bitCountAbs(self: Int) usize {
return usize(@boolToInt(!self.positive)) + u_bit_count; return (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1]));
} }
// Returns the number of bits required to represent the integer in twos-complement form.
//
// If the integer is negative the value returned is the number of bits needed by a signed
// integer to represent the value. If positive the value is the number of bits for an
// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
// one greater than the returned value.
//
// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
fn bitCountTwosComp(self: Int) usize {
var bits = self.bitCountAbs();
// If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos
// complement requires one less bit.
if (!self.positive) block: {
bits += 1;
if (@popCount(self.limbs[self.len - 1]) == 1) {
for (self.limbs[0 .. self.len - 1]) |limb| {
if (@popCount(limb) != 0) {
break :block;
}
}
bits -= 1;
}
}
return bits;
}
pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
if (self.eqZero()) {
return true;
}
if (!is_signed and !self.positive) {
return false;
}
const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and is_signed);
return bit_count >= req_bits;
}
pub fn fits(self: Int, comptime T: type) bool {
return self.fitsInTwosComp(T.is_signed, T.bit_count);
}
// Returns the approximate size of the integer in the given base. Negative values accomodate for
// the minus sign. This is used for determining the number of characters needed to print the
// value. It is inexact and will exceed the given value by 1-2 digits.
pub fn sizeInBase(self: Int, base: usize) usize { pub fn sizeInBase(self: Int, base: usize) usize {
return (self.bitcount() / math.log2(base)) + 1; const bit_count = usize(@boolToInt(!self.positive)) + self.bitCountAbs();
return (bit_count / math.log2(base)) + 1;
} }
pub fn set(self: *Int, value: var) Allocator.Error!void { pub fn set(self: *Int, value: var) Allocator.Error!void {
@ -189,9 +240,9 @@ pub const Int = struct {
pub fn to(self: Int, comptime T: type) ConvertError!T { pub fn to(self: Int, comptime T: type) ConvertError!T {
switch (@typeId(T)) { switch (@typeId(T)) {
TypeId.Int => { TypeId.Int => {
const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T; const UT = @IntType(false, T.bit_count);
if (self.bitcount() > 8 * @sizeOf(UT)) { if (self.bitCountTwosComp() > T.bit_count) {
return error.TargetTooSmall; return error.TargetTooSmall;
} }
@ -208,9 +259,17 @@ pub const Int = struct {
} }
if (!T.is_signed) { if (!T.is_signed) {
return if (self.positive) r else error.NegativeIntoUnsigned; return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned;
} else { } else {
return if (self.positive) @intCast(T, r) else -@intCast(T, r); if (self.positive) {
return @intCast(T, r);
} else {
if (math.cast(T, r)) |ok| {
return -ok;
} else |_| {
return @minValue(T);
}
}
} }
}, },
else => { else => {
@ -274,6 +333,7 @@ pub const Int = struct {
self.positive = positive; self.positive = positive;
} }
/// TODO make this call format instead of the other way around
pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 { pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 {
if (base < 2 or base > 16) { if (base < 2 or base > 16) {
return error.InvalidBase; return error.InvalidBase;
@ -356,6 +416,21 @@ pub const Int = struct {
return s; return s;
} }
/// for the std lib format function
/// TODO make this non-allocating
pub fn format(
self: Int,
comptime fmt: []const u8,
context: var,
comptime FmtError: type,
output: fn (@typeOf(context), []const u8) FmtError!void,
) FmtError!void {
// TODO look at fmt and support other bases
const str = self.toString(self.allocator, 10) catch @panic("TODO make this non allocating");
defer self.allocator.free(str);
return output(context, str);
}
// returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively. // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
pub fn cmpAbs(a: Int, b: Int) i8 { pub fn cmpAbs(a: Int, b: Int) i8 {
if (a.len < b.len) { if (a.len < b.len) {
@ -1120,24 +1195,88 @@ test "big.int bitcount + sizeInBase" {
var a = try Int.init(al); var a = try Int.init(al);
try a.set(0b100); try a.set(0b100);
debug.assert(a.bitcount() == 3); debug.assert(a.bitCountAbs() == 3);
debug.assert(a.sizeInBase(2) >= 3); debug.assert(a.sizeInBase(2) >= 3);
debug.assert(a.sizeInBase(10) >= 1); debug.assert(a.sizeInBase(10) >= 1);
a.negate();
debug.assert(a.bitCountAbs() == 3);
debug.assert(a.sizeInBase(2) >= 4);
debug.assert(a.sizeInBase(10) >= 2);
try a.set(0xffffffff); try a.set(0xffffffff);
debug.assert(a.bitcount() == 32); debug.assert(a.bitCountAbs() == 32);
debug.assert(a.sizeInBase(2) >= 32); debug.assert(a.sizeInBase(2) >= 32);
debug.assert(a.sizeInBase(10) >= 10); debug.assert(a.sizeInBase(10) >= 10);
try a.shiftLeft(a, 5000); try a.shiftLeft(a, 5000);
debug.assert(a.bitcount() == 5032); debug.assert(a.bitCountAbs() == 5032);
debug.assert(a.sizeInBase(2) >= 5032); debug.assert(a.sizeInBase(2) >= 5032);
a.positive = false; a.positive = false;
debug.assert(a.bitcount() == 5033); debug.assert(a.bitCountAbs() == 5032);
debug.assert(a.sizeInBase(2) >= 5033); debug.assert(a.sizeInBase(2) >= 5033);
} }
test "big.int bitcount/to" {
var a = try Int.init(al);
try a.set(0);
debug.assert(a.bitCountTwosComp() == 0);
// TODO: stack smashing
// debug.assert((try a.to(u0)) == 0);
// TODO: sigsegv
// debug.assert((try a.to(i0)) == 0);
try a.set(-1);
debug.assert(a.bitCountTwosComp() == 1);
debug.assert((try a.to(i1)) == -1);
try a.set(-8);
debug.assert(a.bitCountTwosComp() == 4);
debug.assert((try a.to(i4)) == -8);
try a.set(127);
debug.assert(a.bitCountTwosComp() == 7);
debug.assert((try a.to(u7)) == 127);
try a.set(-128);
debug.assert(a.bitCountTwosComp() == 8);
debug.assert((try a.to(i8)) == -128);
try a.set(-129);
debug.assert(a.bitCountTwosComp() == 9);
debug.assert((try a.to(i9)) == -129);
}
test "big.int fits" {
var a = try Int.init(al);
try a.set(0);
debug.assert(a.fits(u0));
debug.assert(a.fits(i0));
try a.set(255);
debug.assert(!a.fits(u0));
debug.assert(!a.fits(u1));
debug.assert(!a.fits(i8));
debug.assert(a.fits(u8));
debug.assert(a.fits(u9));
debug.assert(a.fits(i9));
try a.set(-128);
debug.assert(!a.fits(i7));
debug.assert(a.fits(i8));
debug.assert(a.fits(i9));
debug.assert(!a.fits(u9));
try a.set(0x1ffffffffeeeeeeee);
debug.assert(!a.fits(u32));
debug.assert(!a.fits(u64));
debug.assert(a.fits(u65));
}
test "big.int string set" { test "big.int string set" {
var a = try Int.init(al); var a = try Int.init(al);
try a.setString(10, "120317241209124781241290847124"); try a.setString(10, "120317241209124781241290847124");

View file

@ -35,6 +35,7 @@ pub const Allocator = struct {
freeFn: fn (self: *Allocator, old_mem: []u8) void, freeFn: fn (self: *Allocator, old_mem: []u8) void,
/// Call `destroy` with the result /// Call `destroy` with the result
/// TODO this is deprecated. use createOne instead
pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) { pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) {
const T = @typeOf(init); const T = @typeOf(init);
if (@sizeOf(T) == 0) return &(T{}); if (@sizeOf(T) == 0) return &(T{});
@ -44,6 +45,14 @@ pub const Allocator = struct {
return ptr; return ptr;
} }
/// Call `destroy` with the result.
/// Returns undefined memory.
pub fn createOne(self: *Allocator, comptime T: type) Error!*T {
if (@sizeOf(T) == 0) return &(T{});
const slice = try self.alloc(T, 1);
return &slice[0];
}
/// `ptr` should be the return value of `create` /// `ptr` should be the return value of `create`
pub fn destroy(self: *Allocator, ptr: var) void { pub fn destroy(self: *Allocator, ptr: var) void {
const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr)); const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
@ -149,13 +158,12 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
@setRuntimeSafety(false); @setRuntimeSafety(false);
assert(dest.len >= source.len); assert(dest.len >= source.len);
var i = source.len; var i = source.len;
while(i > 0){ while (i > 0) {
i -= 1; i -= 1;
dest[i] = source[i]; dest[i] = source[i];
} }
} }
pub fn set(comptime T: type, dest: []T, value: T) void { pub fn set(comptime T: type, dest: []T, value: T) void {
for (dest) |*d| for (dest) |*d|
d.* = value; d.* = value;

View file

@ -2,7 +2,7 @@ const std = @import("../index.zig");
const c = std.c; const c = std.c;
const assert = std.debug.assert; const assert = std.debug.assert;
pub use @import("darwin_errno.zig"); pub use @import("darwin/errno.zig");
pub const PATH_MAX = 1024; pub const PATH_MAX = 1024;
@ -482,6 +482,92 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;
/// data is mach absolute time units /// data is mach absolute time units
pub const NOTE_MACHTIME = 0x00000100; pub const NOTE_MACHTIME = 0x00000100;
pub const AF_UNSPEC: c_int = 0;
pub const AF_LOCAL: c_int = 1;
pub const AF_UNIX: c_int = AF_LOCAL;
pub const AF_INET: c_int = 2;
pub const AF_SYS_CONTROL: c_int = 2;
pub const AF_IMPLINK: c_int = 3;
pub const AF_PUP: c_int = 4;
pub const AF_CHAOS: c_int = 5;
pub const AF_NS: c_int = 6;
pub const AF_ISO: c_int = 7;
pub const AF_OSI: c_int = AF_ISO;
pub const AF_ECMA: c_int = 8;
pub const AF_DATAKIT: c_int = 9;
pub const AF_CCITT: c_int = 10;
pub const AF_SNA: c_int = 11;
pub const AF_DECnet: c_int = 12;
pub const AF_DLI: c_int = 13;
pub const AF_LAT: c_int = 14;
pub const AF_HYLINK: c_int = 15;
pub const AF_APPLETALK: c_int = 16;
pub const AF_ROUTE: c_int = 17;
pub const AF_LINK: c_int = 18;
pub const AF_XTP: c_int = 19;
pub const AF_COIP: c_int = 20;
pub const AF_CNT: c_int = 21;
pub const AF_RTIP: c_int = 22;
pub const AF_IPX: c_int = 23;
pub const AF_SIP: c_int = 24;
pub const AF_PIP: c_int = 25;
pub const AF_ISDN: c_int = 28;
pub const AF_E164: c_int = AF_ISDN;
pub const AF_KEY: c_int = 29;
pub const AF_INET6: c_int = 30;
pub const AF_NATM: c_int = 31;
pub const AF_SYSTEM: c_int = 32;
pub const AF_NETBIOS: c_int = 33;
pub const AF_PPP: c_int = 34;
pub const AF_MAX: c_int = 40;
pub const PF_UNSPEC: c_int = AF_UNSPEC;
pub const PF_LOCAL: c_int = AF_LOCAL;
pub const PF_UNIX: c_int = PF_LOCAL;
pub const PF_INET: c_int = AF_INET;
pub const PF_IMPLINK: c_int = AF_IMPLINK;
pub const PF_PUP: c_int = AF_PUP;
pub const PF_CHAOS: c_int = AF_CHAOS;
pub const PF_NS: c_int = AF_NS;
pub const PF_ISO: c_int = AF_ISO;
pub const PF_OSI: c_int = AF_ISO;
pub const PF_ECMA: c_int = AF_ECMA;
pub const PF_DATAKIT: c_int = AF_DATAKIT;
pub const PF_CCITT: c_int = AF_CCITT;
pub const PF_SNA: c_int = AF_SNA;
pub const PF_DECnet: c_int = AF_DECnet;
pub const PF_DLI: c_int = AF_DLI;
pub const PF_LAT: c_int = AF_LAT;
pub const PF_HYLINK: c_int = AF_HYLINK;
pub const PF_APPLETALK: c_int = AF_APPLETALK;
pub const PF_ROUTE: c_int = AF_ROUTE;
pub const PF_LINK: c_int = AF_LINK;
pub const PF_XTP: c_int = AF_XTP;
pub const PF_COIP: c_int = AF_COIP;
pub const PF_CNT: c_int = AF_CNT;
pub const PF_SIP: c_int = AF_SIP;
pub const PF_IPX: c_int = AF_IPX;
pub const PF_RTIP: c_int = AF_RTIP;
pub const PF_PIP: c_int = AF_PIP;
pub const PF_ISDN: c_int = AF_ISDN;
pub const PF_KEY: c_int = AF_KEY;
pub const PF_INET6: c_int = AF_INET6;
pub const PF_NATM: c_int = AF_NATM;
pub const PF_SYSTEM: c_int = AF_SYSTEM;
pub const PF_NETBIOS: c_int = AF_NETBIOS;
pub const PF_PPP: c_int = AF_PPP;
pub const PF_MAX: c_int = AF_MAX;
pub const SYSPROTO_EVENT: c_int = 1;
pub const SYSPROTO_CONTROL: c_int = 2;
pub const SOCK_STREAM: c_int = 1;
pub const SOCK_DGRAM: c_int = 2;
pub const SOCK_RAW: c_int = 3;
pub const SOCK_RDM: c_int = 4;
pub const SOCK_SEQPACKET: c_int = 5;
pub const SOCK_MAXADDRLEN: c_int = 255;
fn wstatus(x: i32) i32 { fn wstatus(x: i32) i32 {
return x & 0o177; return x & 0o177;
} }

View file

@ -15,7 +15,7 @@ pub const File = struct {
/// The OS-specific file descriptor or file handle. /// The OS-specific file descriptor or file handle.
handle: os.FileHandle, handle: os.FileHandle,
const OpenError = os.WindowsOpenError || os.PosixOpenError; pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
/// Call close to clean up. /// Call close to clean up.
@ -109,43 +109,42 @@ pub const File = struct {
Unexpected, Unexpected,
}; };
pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) AccessError!bool { pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void {
const path_with_null = try std.cstr.addNullByte(allocator, path); const path_with_null = try std.cstr.addNullByte(allocator, path);
defer allocator.free(path_with_null); defer allocator.free(path_with_null);
if (is_posix) { if (is_posix) {
// mode is ignored and is always F_OK for now
const result = posix.access(path_with_null.ptr, posix.F_OK); const result = posix.access(path_with_null.ptr, posix.F_OK);
const err = posix.getErrno(result); const err = posix.getErrno(result);
if (err > 0) { switch (err) {
return switch (err) { 0 => return,
posix.EACCES => error.PermissionDenied, posix.EACCES => return error.PermissionDenied,
posix.EROFS => error.PermissionDenied, posix.EROFS => return error.PermissionDenied,
posix.ELOOP => error.PermissionDenied, posix.ELOOP => return error.PermissionDenied,
posix.ETXTBSY => error.PermissionDenied, posix.ETXTBSY => return error.PermissionDenied,
posix.ENOTDIR => error.NotFound, posix.ENOTDIR => return error.NotFound,
posix.ENOENT => error.NotFound, posix.ENOENT => return error.NotFound,
posix.ENAMETOOLONG => error.NameTooLong, posix.ENAMETOOLONG => return error.NameTooLong,
posix.EINVAL => error.BadMode, posix.EINVAL => unreachable,
posix.EFAULT => error.BadPathName, posix.EFAULT => return error.BadPathName,
posix.EIO => error.Io, posix.EIO => return error.Io,
posix.ENOMEM => error.SystemResources, posix.ENOMEM => return error.SystemResources,
else => os.unexpectedErrorPosix(err), else => return os.unexpectedErrorPosix(err),
};
} }
return true;
} else if (is_windows) { } else if (is_windows) {
if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) { if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
return true; return;
} }
const err = windows.GetLastError(); const err = windows.GetLastError();
return switch (err) { switch (err) {
windows.ERROR.FILE_NOT_FOUND => error.NotFound, windows.ERROR.FILE_NOT_FOUND,
windows.ERROR.ACCESS_DENIED => error.PermissionDenied, windows.ERROR.PATH_NOT_FOUND,
else => os.unexpectedErrorWindows(err), => return error.NotFound,
}; windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
else => return os.unexpectedErrorWindows(err),
}
} else { } else {
@compileError("TODO implement access for this OS"); @compileError("TODO implement access for this OS");
} }
@ -242,7 +241,7 @@ pub const File = struct {
}, },
Os.windows => { Os.windows => {
var pos: windows.LARGE_INTEGER = undefined; var pos: windows.LARGE_INTEGER = undefined;
if (windows.SetFilePointerEx(self.handle, 0, *pos, windows.FILE_CURRENT) == 0) { if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {
const err = windows.GetLastError(); const err = windows.GetLastError();
return switch (err) { return switch (err) {
windows.ERROR.INVALID_PARAMETER => error.BadFd, windows.ERROR.INVALID_PARAMETER => error.BadFd,
@ -251,13 +250,7 @@ pub const File = struct {
} }
assert(pos >= 0); assert(pos >= 0);
if (@sizeOf(@typeOf(pos)) > @sizeOf(usize)) { return math.cast(usize, pos) catch error.FilePosLargerThanPointerRange;
if (pos > @maxValue(usize)) {
return error.FilePosLargerThanPointerRange;
}
}
return usize(pos);
}, },
else => @compileError("unsupported OS"), else => @compileError("unsupported OS"),
} }
@ -289,7 +282,7 @@ pub const File = struct {
Unexpected, Unexpected,
}; };
fn mode(self: *File) ModeError!os.FileMode { pub fn mode(self: *File) ModeError!os.FileMode {
if (is_posix) { if (is_posix) {
var stat: posix.Stat = undefined; var stat: posix.Stat = undefined;
const err = posix.getErrno(posix.fstat(self.handle, &stat)); const err = posix.getErrno(posix.fstat(self.handle, &stat));
@ -364,7 +357,7 @@ pub const File = struct {
pub const WriteError = os.WindowsWriteError || os.PosixWriteError; pub const WriteError = os.WindowsWriteError || os.PosixWriteError;
fn write(self: *File, bytes: []const u8) WriteError!void { pub fn write(self: *File, bytes: []const u8) WriteError!void {
if (is_posix) { if (is_posix) {
try os.posixWrite(self.handle, bytes); try os.posixWrite(self.handle, bytes);
} else if (is_windows) { } else if (is_windows) {

View file

@ -0,0 +1,69 @@
const std = @import("../index.zig");
const builtin = @import("builtin");
const unicode = std.unicode;
const mem = std.mem;
const os = std.os;
pub const GetAppDataDirError = error{
OutOfMemory,
AppDataDirUnavailable,
};
/// Caller owns returned memory.
pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
switch (builtin.os) {
builtin.Os.windows => {
var dir_path_ptr: [*]u16 = undefined;
switch (os.windows.SHGetKnownFolderPath(
&os.windows.FOLDERID_LocalAppData,
os.windows.KF_FLAG_CREATE,
null,
&dir_path_ptr,
)) {
os.windows.S_OK => {
defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));
const global_dir = unicode.utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) {
error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
error.DanglingSurrogateHalf => return error.AppDataDirUnavailable,
error.OutOfMemory => return error.OutOfMemory,
};
defer allocator.free(global_dir);
return os.path.join(allocator, global_dir, appname);
},
os.windows.E_OUTOFMEMORY => return error.OutOfMemory,
else => return error.AppDataDirUnavailable,
}
},
builtin.Os.macosx => {
const home_dir = os.getEnvPosix("HOME") orelse {
// TODO look in /etc/passwd
return error.AppDataDirUnavailable;
};
return os.path.join(allocator, home_dir, "Library", "Application Support", appname);
},
builtin.Os.linux => {
const home_dir = os.getEnvPosix("HOME") orelse {
// TODO look in /etc/passwd
return error.AppDataDirUnavailable;
};
return os.path.join(allocator, home_dir, ".local", "share", appname);
},
else => @compileError("Unsupported OS"),
}
}
fn utf16lePtrSlice(ptr: [*]const u16) []const u16 {
var index: usize = 0;
while (ptr[index] != 0) : (index += 1) {}
return ptr[0..index];
}
test "std.os.getAppDataDir" {
var buf: [512]u8 = undefined;
const allocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
// We can't actually validate the result
_ = getAppDataDir(allocator, "zig") catch return;
}

View file

@ -11,13 +11,14 @@ const os = this;
test "std.os" { test "std.os" {
_ = @import("child_process.zig"); _ = @import("child_process.zig");
_ = @import("darwin.zig"); _ = @import("darwin.zig");
_ = @import("darwin_errno.zig"); _ = @import("darwin/errno.zig");
_ = @import("get_user_id.zig"); _ = @import("get_user_id.zig");
_ = @import("linux/index.zig"); _ = @import("linux/index.zig");
_ = @import("path.zig"); _ = @import("path.zig");
_ = @import("test.zig"); _ = @import("test.zig");
_ = @import("time.zig"); _ = @import("time.zig");
_ = @import("windows/index.zig"); _ = @import("windows/index.zig");
_ = @import("get_app_data_dir.zig");
} }
pub const windows = @import("windows/index.zig"); pub const windows = @import("windows/index.zig");
@ -76,6 +77,9 @@ pub const WindowsWriteError = windows_util.WriteError;
pub const FileHandle = if (is_windows) windows.HANDLE else i32; pub const FileHandle = if (is_windows) windows.HANDLE else i32;
pub const getAppDataDir = @import("get_app_data_dir.zig").getAppDataDir;
pub const GetAppDataDirError = @import("get_app_data_dir.zig").GetAppDataDirError;
const debug = std.debug; const debug = std.debug;
const assert = debug.assert; const assert = debug.assert;
@ -494,6 +498,7 @@ pub var linux_aux_raw = []usize{0} ** 38;
pub var posix_environ_raw: [][*]u8 = undefined; pub var posix_environ_raw: [][*]u8 = undefined;
/// Caller must free result when done. /// Caller must free result when done.
/// TODO make this go through libc when we have it
pub fn getEnvMap(allocator: *Allocator) !BufMap { pub fn getEnvMap(allocator: *Allocator) !BufMap {
var result = BufMap.init(allocator); var result = BufMap.init(allocator);
errdefer result.deinit(); errdefer result.deinit();
@ -537,6 +542,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
} }
} }
/// TODO make this go through libc when we have it
pub fn getEnvPosix(key: []const u8) ?[]const u8 { pub fn getEnvPosix(key: []const u8) ?[]const u8 {
for (posix_environ_raw) |ptr| { for (posix_environ_raw) |ptr| {
var line_i: usize = 0; var line_i: usize = 0;
@ -559,6 +565,7 @@ pub const GetEnvVarOwnedError = error{
}; };
/// Caller must free returned memory. /// Caller must free returned memory.
/// TODO make this go through libc when we have it
pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {
if (is_windows) { if (is_windows) {
const key_with_null = try cstr.addNullByte(allocator, key); const key_with_null = try cstr.addNullByte(allocator, key);

View file

@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" {
test "access file" { test "access file" {
try os.makePath(a, "os_test_tmp"); try os.makePath(a, "os_test_tmp");
if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| { if (os.File.access(a, "os_test_tmp/file.txt")) |ok| {
unreachable; @panic("expected error");
} else |err| { } else |err| {
assert(err == error.NotFound); assert(err == error.NotFound);
} }
try io.writeFile(a, "os_test_tmp/file.txt", ""); try io.writeFile(a, "os_test_tmp/file.txt", "");
assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true); try os.File.access(a, "os_test_tmp/file.txt");
try os.deleteTree(a, "os_test_tmp"); try os.deleteTree(a, "os_test_tmp");
} }

View file

@ -0,0 +1,30 @@
use @import("index.zig");
pub const PROV_RSA_FULL = 1;
pub const REGSAM = ACCESS_MASK;
pub const ACCESS_MASK = DWORD;
pub const PHKEY = &HKEY;
pub const HKEY = &HKEY__;
pub const HKEY__ = extern struct {
unused: c_int,
};
pub const LSTATUS = LONG;
pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
phProv: *HCRYPTPROV,
pszContainer: ?LPCSTR,
pszProvider: ?LPCSTR,
dwProvType: DWORD,
dwFlags: DWORD,
) BOOL;
pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL;
pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM,
phkResult: &HKEY,) LSTATUS;
pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD,
lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS;

View file

@ -1,188 +1,19 @@
const std = @import("../../index.zig");
const assert = std.debug.assert;
pub use @import("advapi32.zig");
pub use @import("kernel32.zig");
pub use @import("ole32.zig");
pub use @import("shell32.zig");
pub use @import("shlwapi.zig");
pub use @import("user32.zig");
test "import" { test "import" {
_ = @import("util.zig"); _ = @import("util.zig");
} }
pub const ERROR = @import("error.zig"); pub const ERROR = @import("error.zig");
pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
phProv: *HCRYPTPROV,
pszContainer: ?LPCSTR,
pszProvider: ?LPCSTR,
dwProvType: DWORD,
dwFlags: DWORD,
) BOOL;
pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL;
pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn CreateDirectoryA(
lpPathName: LPCSTR,
lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateFileA(
lpFileName: LPCSTR,
dwDesiredAccess: DWORD,
dwShareMode: DWORD,
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
dwCreationDisposition: DWORD,
dwFlagsAndAttributes: DWORD,
hTemplateFile: ?HANDLE,
) HANDLE;
pub extern "kernel32" stdcallcc fn CreatePipe(
hReadPipe: *HANDLE,
hWritePipe: *HANDLE,
lpPipeAttributes: *const SECURITY_ATTRIBUTES,
nSize: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateProcessA(
lpApplicationName: ?LPCSTR,
lpCommandLine: LPSTR,
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
bInheritHandles: BOOL,
dwCreationFlags: DWORD,
lpEnvironment: ?*c_void,
lpCurrentDirectory: ?LPCSTR,
lpStartupInfo: *STARTUPINFOA,
lpProcessInformation: *PROCESS_INFORMATION,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
lpSymlinkFileName: LPCSTR,
lpTargetFileName: LPCSTR,
dwFlags: DWORD,
) BOOLEAN;
pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
in_hFile: HANDLE,
in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
out_lpFileInformation: *c_void,
in_dwBufferSize: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
hFile: HANDLE,
lpszFilePath: LPSTR,
cchFilePath: DWORD,
dwFlags: DWORD,
) DWORD;
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
pub extern "kernel32" stdcallcc fn MoveFileExA(
lpExistingFileName: LPCSTR,
lpNewFileName: LPCSTR,
dwFlags: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn ReadFile(
in_hFile: HANDLE,
out_lpBuffer: *c_void,
in_nNumberOfBytesToRead: DWORD,
out_lpNumberOfBytesRead: *DWORD,
in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL;
pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn SetFilePointerEx(
in_fFile: HANDLE,
in_liDistanceToMove: LARGE_INTEGER,
out_opt_ldNewFilePointer: ?*LARGE_INTEGER,
in_dwMoveMethod: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL;
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 WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn WriteFile(
in_hFile: HANDLE,
in_lpBuffer: *const c_void,
in_nNumberOfBytesToWrite: DWORD,
out_lpNumberOfBytesWritten: ?*DWORD,
in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL;
//TODO: call unicode versions instead of relying on ANSI code page
pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
pub const PROV_RSA_FULL = 1;
pub const BOOL = c_int; pub const BOOL = c_int;
pub const BOOLEAN = BYTE; pub const BOOLEAN = BYTE;
pub const BYTE = u8; pub const BYTE = u8;
@ -204,6 +35,7 @@ pub const LPSTR = [*]CHAR;
pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR;
pub const LPVOID = *c_void; pub const LPVOID = *c_void;
pub const LPWSTR = [*]WCHAR; pub const LPWSTR = [*]WCHAR;
pub const LPCWSTR = [*]const WCHAR;
pub const PVOID = *c_void; pub const PVOID = *c_void;
pub const PWSTR = [*]WCHAR; pub const PWSTR = [*]WCHAR;
pub const SIZE_T = usize; pub const SIZE_T = usize;
@ -439,3 +271,82 @@ pub const SYSTEM_INFO = extern struct {
wProcessorLevel: WORD, wProcessorLevel: WORD,
wProcessorRevision: WORD, wProcessorRevision: WORD,
}; };
pub const HRESULT = c_long;
pub const KNOWNFOLDERID = GUID;
pub const GUID = extern struct {
Data1: c_ulong,
Data2: c_ushort,
Data3: c_ushort,
Data4: [8]u8,
pub fn parse(str: []const u8) GUID {
var guid: GUID = undefined;
var index: usize = 0;
assert(str[index] == '{');
index += 1;
guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index..index + 8], 16) catch unreachable;
index += 8;
assert(str[index] == '-');
index += 1;
guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable;
index += 4;
assert(str[index] == '-');
index += 1;
guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable;
index += 4;
assert(str[index] == '-');
index += 1;
guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
index += 2;
guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
index += 2;
assert(str[index] == '-');
index += 1;
var i: usize = 2;
while (i < guid.Data4.len) : (i += 1) {
guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
index += 2;
}
assert(str[index] == '}');
index += 1;
return guid;
}
};
pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}");
pub const KF_FLAG_DEFAULT = 0;
pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536;
pub const KF_FLAG_CREATE = 32768;
pub const KF_FLAG_DONT_VERIFY = 16384;
pub const KF_FLAG_DONT_UNEXPAND = 8192;
pub const KF_FLAG_NO_ALIAS = 4096;
pub const KF_FLAG_INIT = 2048;
pub const KF_FLAG_DEFAULT_PATH = 1024;
pub const KF_FLAG_NOT_PARENT_RELATIVE = 512;
pub const KF_FLAG_SIMPLE_IDLIST = 256;
pub const KF_FLAG_ALIAS_ONLY = -2147483648;
pub const S_OK = 0;
pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001));
pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002));
pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003));
pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004));
pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005));
pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF));
pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));
pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));
pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));
pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057));

162
std/os/windows/kernel32.zig Normal file
View file

@ -0,0 +1,162 @@
use @import("index.zig");
pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn CreateDirectoryA(
lpPathName: LPCSTR,
lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateFileA(
lpFileName: LPCSTR,
dwDesiredAccess: DWORD,
dwShareMode: DWORD,
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
dwCreationDisposition: DWORD,
dwFlagsAndAttributes: DWORD,
hTemplateFile: ?HANDLE,
) HANDLE;
pub extern "kernel32" stdcallcc fn CreatePipe(
hReadPipe: *HANDLE,
hWritePipe: *HANDLE,
lpPipeAttributes: *const SECURITY_ATTRIBUTES,
nSize: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateProcessA(
lpApplicationName: ?LPCSTR,
lpCommandLine: LPSTR,
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
bInheritHandles: BOOL,
dwCreationFlags: DWORD,
lpEnvironment: ?*c_void,
lpCurrentDirectory: ?LPCSTR,
lpStartupInfo: *STARTUPINFOA,
lpProcessInformation: *PROCESS_INFORMATION,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
lpSymlinkFileName: LPCSTR,
lpTargetFileName: LPCSTR,
dwFlags: DWORD,
) BOOLEAN;
pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
in_hFile: HANDLE,
in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
out_lpFileInformation: *c_void,
in_dwBufferSize: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
hFile: HANDLE,
lpszFilePath: LPSTR,
cchFilePath: DWORD,
dwFlags: DWORD,
) DWORD;
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
pub extern "kernel32" stdcallcc fn MoveFileExA(
lpExistingFileName: LPCSTR,
lpNewFileName: LPCSTR,
dwFlags: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn ReadFile(
in_hFile: HANDLE,
out_lpBuffer: *c_void,
in_nNumberOfBytesToRead: DWORD,
out_lpNumberOfBytesRead: *DWORD,
in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL;
pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn SetFilePointerEx(
in_fFile: HANDLE,
in_liDistanceToMove: LARGE_INTEGER,
out_opt_ldNewFilePointer: ?*LARGE_INTEGER,
in_dwMoveMethod: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL;
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 WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn WriteFile(
in_hFile: HANDLE,
in_lpBuffer: *const c_void,
in_nNumberOfBytesToWrite: DWORD,
out_lpNumberOfBytesWritten: ?*DWORD,
in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL;
//TODO: call unicode versions instead of relying on ANSI code page
pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;

18
std/os/windows/ole32.zig Normal file
View file

@ -0,0 +1,18 @@
use @import("index.zig");
pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void;
pub extern "ole32.dll" stdcallcc fn CoUninitialize() void;
pub extern "ole32.dll" stdcallcc fn CoGetCurrentProcess() DWORD;
pub extern "ole32.dll" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT;
pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED;
pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED;
pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE;
pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY;
pub const COINIT = extern enum {
COINIT_APARTMENTTHREADED = 2,
COINIT_MULTITHREADED = 0,
COINIT_DISABLE_OLE1DDE = 4,
COINIT_SPEED_OVER_MEMORY = 8,
};

View file

@ -0,0 +1,4 @@
use @import("index.zig");
pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT;

View file

@ -0,0 +1,4 @@
use @import("index.zig");
pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;

View file

@ -0,0 +1,4 @@
use @import("index.zig");
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;

View file

@ -5,11 +5,25 @@ const test_fn_list = builtin.__zig_test_fn_slice;
const warn = std.debug.warn; const warn = std.debug.warn;
pub fn main() !void { pub fn main() !void {
var ok_count: usize = 0;
var skip_count: usize = 0;
for (test_fn_list) |test_fn, i| { for (test_fn_list) |test_fn, i| {
warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name); warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
try test_fn.func(); if (test_fn.func()) |_| {
ok_count += 1;
warn("OK\n"); warn("OK\n");
} else |err| switch (err) {
error.SkipZigTest => {
skip_count += 1;
warn("SKIP\n");
},
else => return err,
}
}
if (ok_count == test_fn_list.len) {
warn("All tests passed.\n");
} else {
warn("{} passed; {} skipped.\n", ok_count, skip_count);
} }
} }

View file

@ -1,5 +1,8 @@
const std = @import("./index.zig"); const std = @import("./index.zig");
const builtin = @import("builtin");
const debug = std.debug; const debug = std.debug;
const assert = std.debug.assert;
const mem = std.mem;
/// Returns how many bytes the UTF-8 representation would require /// Returns how many bytes the UTF-8 representation would require
/// for the given codepoint. /// for the given codepoint.
@ -441,3 +444,89 @@ fn testDecode(bytes: []const u8) !u32 {
debug.assert(bytes.len == length); debug.assert(bytes.len == length);
return utf8Decode(bytes); return utf8Decode(bytes);
} }
// TODO: make this API on top of a non-allocating Utf16LeView
pub fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
var result = std.ArrayList(u8).init(allocator);
// optimistically guess that it will all be ascii.
try result.ensureCapacity(utf16le.len);
const utf16le_as_bytes = @sliceToBytes(utf16le);
var i: usize = 0;
var out_index: usize = 0;
while (i < utf16le_as_bytes.len) : (i += 2) {
// decode
const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
var codepoint: u32 = undefined;
if (c0 & ~u32(0x03ff) == 0xd800) {
// surrogate pair
i += 2;
if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf;
const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
} else if (c0 & ~u32(0x03ff) == 0xdc00) {
return error.UnexpectedSecondSurrogateHalf;
} else {
codepoint = c0;
}
// encode
const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;
try result.resize(result.len + utf8_len);
_ = utf8Encode(codepoint, result.items[out_index..]) catch unreachable;
out_index += utf8_len;
}
return result.toOwnedSlice();
}
test "utf16leToUtf8" {
var utf16le: [2]u16 = undefined;
const utf16le_as_bytes = @sliceToBytes(utf16le[0..]);
{
mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little);
mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little);
const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
assert(mem.eql(u8, utf8, "Aa"));
}
{
mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little);
mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little);
const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf"));
}
{
// the values just outside the surrogate half range
mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little);
mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little);
const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80"));
}
{
// smallest surrogate pair
mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little);
mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);
const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80"));
}
{
// largest surrogate pair
mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little);
const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf"));
}
{
mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);
const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));
}
}

View file

@ -2,6 +2,7 @@ const tokenizer = @import("tokenizer.zig");
pub const Token = tokenizer.Token; pub const Token = tokenizer.Token;
pub const Tokenizer = tokenizer.Tokenizer; pub const Tokenizer = tokenizer.Tokenizer;
pub const parse = @import("parse.zig").parse; pub const parse = @import("parse.zig").parse;
pub const parseStringLiteral = @import("parse_string_literal.zig").parseStringLiteral;
pub const render = @import("render.zig").render; pub const render = @import("render.zig").render;
pub const ast = @import("ast.zig"); pub const ast = @import("ast.zig");
@ -10,4 +11,6 @@ test "std.zig tests" {
_ = @import("parse.zig"); _ = @import("parse.zig");
_ = @import("render.zig"); _ = @import("render.zig");
_ = @import("tokenizer.zig"); _ = @import("tokenizer.zig");
_ = @import("parse_string_literal.zig");
} }

View file

@ -2356,7 +2356,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
const token = nextToken(&tok_it, &tree); const token = nextToken(&tok_it, &tree);
switch (token.ptr.id) { switch (token.ptr.id) {
Token.Id.IntegerLiteral => { Token.Id.IntegerLiteral => {
_ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token.index); _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.IntegerLiteral, token.index);
continue; continue;
}, },
Token.Id.FloatLiteral => { Token.Id.FloatLiteral => {

View file

@ -0,0 +1,76 @@
const std = @import("../index.zig");
const assert = std.debug.assert;
const State = enum {
Start,
Backslash,
};
pub const ParseStringLiteralError = error{
OutOfMemory,
/// When this is returned, index will be the position of the character.
InvalidCharacter,
};
/// caller owns returned memory
pub fn parseStringLiteral(
allocator: *std.mem.Allocator,
bytes: []const u8,
bad_index: *usize, // populated if error.InvalidCharacter is returned
) ParseStringLiteralError![]u8 {
const first_index = if (bytes[0] == 'c') usize(2) else usize(1);
assert(bytes[bytes.len - 1] == '"');
var list = std.ArrayList(u8).init(allocator);
errdefer list.deinit();
const slice = bytes[first_index..];
try list.ensureCapacity(slice.len - 1);
var state = State.Start;
for (slice) |b, index| {
switch (state) {
State.Start => switch (b) {
'\\' => state = State.Backslash,
'\n' => {
bad_index.* = index;
return error.InvalidCharacter;
},
'"' => return list.toOwnedSlice(),
else => try list.append(b),
},
State.Backslash => switch (b) {
'x' => @panic("TODO"),
'u' => @panic("TODO"),
'U' => @panic("TODO"),
'n' => {
try list.append('\n');
state = State.Start;
},
'r' => {
try list.append('\r');
state = State.Start;
},
'\\' => {
try list.append('\\');
state = State.Start;
},
't' => {
try list.append('\t');
state = State.Start;
},
'"' => {
try list.append('"');
state = State.Start;
},
else => {
bad_index.* = index;
return error.InvalidCharacter;
},
},
else => unreachable,
}
}
unreachable;
}

View file

@ -73,6 +73,7 @@ pub const Token = struct {
return null; return null;
} }
/// TODO remove this enum
const StrLitKind = enum { const StrLitKind = enum {
Normal, Normal,
C, C,

View file

@ -468,3 +468,20 @@ test "@intCast i32 to u7" {
var z = x >> @intCast(u7, y); var z = x >> @intCast(u7, y);
assert(z == 0xff); assert(z == 0xff);
} }
test "implicit cast undefined to optional" {
assert(MakeType(void).getNull() == null);
assert(MakeType(void).getNonNull() != null);
}
fn MakeType(comptime T: type) type {
return struct {
fn getNull() ?T {
return null;
}
fn getNonNull() ?T {
return T(undefined);
}
};
}

View file

@ -61,3 +61,18 @@ test "defer and labeled break" {
assert(i == 1); assert(i == 1);
} }
test "errdefer does not apply to fn inside fn" {
if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad);
}
fn testNestedFnErrDefer() error!void {
var a: i32 = 0;
errdefer a += 1;
const S = struct {
fn baz() error {
return error.Bad;
}
};
return S.baz();
}

View file

@ -642,3 +642,13 @@ test "@tagName of @typeId" {
const str = @tagName(@typeId(u8)); const str = @tagName(@typeId(u8));
assert(std.mem.eql(u8, str, "Int")); assert(std.mem.eql(u8, str, "Int"));
} }
test "setting backward branch quota just before a generic fn call" {
@setEvalBranchQuota(1001);
loopNTimes(1001);
}
fn loopNTimes(comptime n: usize) void {
comptime var i = 0;
inline while (i < n) : (i += 1) {}
}

View file

@ -76,4 +76,51 @@ pub fn addCases(cases: *tests.GenHContext) void {
\\TEST_EXPORT void entry(struct Foo foo, uint8_t bar[]); \\TEST_EXPORT void entry(struct Foo foo, uint8_t bar[]);
\\ \\
); );
cases.add("ptr to zig struct",
\\const S = struct {
\\ a: u8,
\\};
\\
\\export fn a(s: *S) u8 {
\\ return s.a;
\\}
,
\\struct S;
\\TEST_EXPORT uint8_t a(struct S * s);
\\
);
cases.add("ptr to zig union",
\\const U = union(enum) {
\\ A: u8,
\\ B: u16,
\\};
\\
\\export fn a(s: *U) u8 {
\\ return s.A;
\\}
,
\\union U;
\\TEST_EXPORT uint8_t a(union U * s);
\\
);
cases.add("ptr to zig enum",
\\const E = enum(u8) {
\\ A,
\\ B,
\\};
\\
\\export fn a(s: *E) u8 {
\\ return @enumToInt(s.*);
\\}
,
\\enum E;
\\TEST_EXPORT uint8_t a(enum E * s);
\\
);
} }

View file

@ -0,0 +1,12 @@
const std = @import("std");
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
pub fn addCases(ctx: *TestContext) !void {
try ctx.testCompareOutputLibC(
\\extern fn puts([*]const u8) void;
\\export fn main() c_int {
\\ puts(c"Hello, world!");
\\ return 0;
\\}
, "Hello, world!" ++ std.cstr.line_sep);
}

View file

@ -9,4 +9,22 @@ pub fn addCases(ctx: *TestContext) !void {
try ctx.testCompileError( try ctx.testCompileError(
\\fn() void {} \\fn() void {}
, "1.zig", 1, 1, "missing function name"); , "1.zig", 1, 1, "missing function name");
try ctx.testCompileError(
\\comptime {
\\ return;
\\}
, "1.zig", 2, 5, "return expression outside function definition");
try ctx.testCompileError(
\\export fn entry() void {
\\ defer return;
\\}
, "1.zig", 2, 11, "cannot return from defer expression");
try ctx.testCompileError(
\\export fn entry() c_int {
\\ return 36893488147419103232;
\\}
, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
} }

View file

@ -89,12 +89,13 @@ pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes:
return cases.step; return cases.step;
} }
pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
const cases = b.allocator.create(BuildExamplesContext{ const cases = b.allocator.create(BuildExamplesContext{
.b = b, .b = b,
.step = b.step("test-build-examples", "Build the examples"), .step = b.step("test-build-examples", "Build the examples"),
.test_index = 0, .test_index = 0,
.test_filter = test_filter, .test_filter = test_filter,
.modes = modes,
}) catch unreachable; }) catch unreachable;
build_examples.addCases(cases); build_examples.addCases(cases);
@ -697,6 +698,7 @@ pub const BuildExamplesContext = struct {
step: *build.Step, step: *build.Step,
test_index: usize, test_index: usize,
test_filter: ?[]const u8, test_filter: ?[]const u8,
modes: []const Mode,
pub fn addC(self: *BuildExamplesContext, root_src: []const u8) void { pub fn addC(self: *BuildExamplesContext, root_src: []const u8) void {
self.addAllArgs(root_src, true); self.addAllArgs(root_src, true);
@ -739,12 +741,7 @@ pub const BuildExamplesContext = struct {
pub fn addAllArgs(self: *BuildExamplesContext, root_src: []const u8, link_libc: bool) void { pub fn addAllArgs(self: *BuildExamplesContext, root_src: []const u8, link_libc: bool) void {
const b = self.b; const b = self.b;
for ([]Mode{ for (self.modes) |mode| {
Mode.Debug,
Mode.ReleaseSafe,
Mode.ReleaseFast,
Mode.ReleaseSmall,
}) |mode| {
const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", root_src, @tagName(mode)) catch unreachable; const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", root_src, @tagName(mode)) catch unreachable;
if (self.test_filter) |filter| { if (self.test_filter) |filter| {
if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;