mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
Merge remote-tracking branch 'origin/master' into llvm7
This commit is contained in:
commit
dd9728c5a0
75 changed files with 7546 additions and 1486 deletions
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
|
|
|
||||||
|
|
@ -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
68
src-self-hosted/c_int.zig
Normal 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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -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
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
462
src-self-hosted/libc_installation.zig
Normal file
462
src-self-hosted/libc_installation.zig
Normal 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
724
src-self-hosted/link.zig
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
29
src-self-hosted/package.zig
Normal file
29
src-self-hosted/package.zig
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
const ast = @import("std").zig.ast;
|
|
||||||
|
|
||||||
pub const ParsedFile = struct {
|
|
||||||
tree: ast.Tree,
|
|
||||||
realpath: []const u8,
|
|
||||||
};
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
106
src/codegen.cpp
106
src/codegen.cpp
|
|
@ -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));
|
||||||
|
|
|
||||||
89
src/ir.cpp
89
src/ir.cpp
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
248
src/os.cpp
248
src/os.cpp
|
|
@ -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\\");
|
||||||
|
|
|
||||||
10
src/os.hpp
10
src/os.hpp
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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
352
src/windows_sdk.cpp
Normal 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
47
src/windows_sdk.h
Normal 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
|
||||||
|
|
@ -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)));
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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"),
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
85
std/lazy_init.zig
Normal 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);
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
12
std/mem.zig
12
std/mem.zig
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
69
std/os/get_app_data_dir.zig
Normal file
69
std/os/get_app_data_dir.zig
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
30
std/os/windows/advapi32.zig
Normal file
30
std/os/windows/advapi32.zig
Normal 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;
|
||||||
|
|
@ -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
162
std/os/windows/kernel32.zig
Normal 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
18
std/os/windows/ole32.zig
Normal 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,
|
||||||
|
};
|
||||||
4
std/os/windows/shell32.zig
Normal file
4
std/os/windows/shell32.zig
Normal 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;
|
||||||
|
|
||||||
4
std/os/windows/shlwapi.zig
Normal file
4
std/os/windows/shlwapi.zig
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
use @import("index.zig");
|
||||||
|
|
||||||
|
pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
|
||||||
|
|
||||||
4
std/os/windows/user32.zig
Normal file
4
std/os/windows/user32.zig
Normal 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;
|
||||||
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 => {
|
||||||
|
|
|
||||||
76
std/zig/parse_string_literal.zig
Normal file
76
std/zig/parse_string_literal.zig
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
\\
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
test/stage2/compare_output.zig
Normal file
12
test/stage2/compare_output.zig
Normal 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);
|
||||||
|
}
|
||||||
|
|
@ -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'");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue