From 7ec783876a565662223268a70ba984e0a132b94a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 11 Jan 2018 23:04:08 -0500
Subject: [PATCH 01/25] functions which can return errors have secret stack
trace param
See #651
---
src/all_types.hpp | 2 ++
src/analyze.cpp | 39 ++++++++++++++++++++++++++++++++++++---
src/analyze.hpp | 4 ++++
src/codegen.cpp | 29 ++++++++++++++++++++++-------
src/ir.cpp | 10 ----------
5 files changed, 64 insertions(+), 20 deletions(-)
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 024c78eb73b8df964f263772031a528b127a1458..caae13d20d4a495146ab7f66308f350fff07d48f 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1595,6 +1595,8 @@ struct CodeGen {
ZigList tld_ref_source_node_stack;
TypeTableEntry *align_amt_type;
+ TypeTableEntry *stack_trace_type;
+ TypeTableEntry *ptr_to_stack_trace_type;
};
enum VarLinkage {
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 7e4a861f0f5e6436bb045bc80ec63571d79fdd2a..2e51e5151cae89d7ecc1d560b999820c49dc27c5 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -869,6 +869,16 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) {
zig_unreachable();
}
+static TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) {
+ if (g->stack_trace_type == nullptr) {
+ ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace");
+ assert(stack_trace_type_val->type->id == TypeTableEntryIdMetaType);
+ g->stack_trace_type = stack_trace_type_val->data.x_type;
+ g->ptr_to_stack_trace_type = get_pointer_to_type(g, g->stack_trace_type, false);
+ }
+ return g->ptr_to_stack_trace_type;
+}
+
TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
if (table_entry) {
@@ -915,10 +925,15 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
if (!skip_debug_info) {
bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) &&
handle_is_ptr(fn_type_id->return_type);
+ bool last_arg_error_return_trace = fn_type_id->return_type->id == TypeTableEntryIdErrorUnion ||
+ fn_type_id->return_type->id == TypeTableEntryIdPureError;
// +1 for maybe making the first argument the return value
- LLVMTypeRef *gen_param_types = allocate(1 + fn_type_id->param_count);
- // +1 because 0 is the return type and +1 for maybe making first arg ret val
- ZigLLVMDIType **param_di_types = allocate(2 + fn_type_id->param_count);
+ // +1 for maybe last argument the error return trace
+ LLVMTypeRef *gen_param_types = allocate(2 + fn_type_id->param_count);
+ // +1 because 0 is the return type and
+ // +1 for maybe making first arg ret val and
+ // +1 for maybe last argument the error return trace
+ ZigLLVMDIType **param_di_types = allocate(3 + fn_type_id->param_count);
param_di_types[0] = fn_type_id->return_type->di_type;
size_t gen_param_index = 0;
TypeTableEntry *gen_return_type;
@@ -965,6 +980,14 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
}
}
+ if (last_arg_error_return_trace) {
+ TypeTableEntry *gen_type = get_ptr_to_stack_trace_type(g);
+ gen_param_types[gen_param_index] = gen_type->type_ref;
+ gen_param_index += 1;
+ // after the gen_param_index += 1 because 0 is the return type
+ param_di_types[gen_param_index] = gen_type->di_type;
+ }
+
fn_type->data.fn.gen_param_count = gen_param_index;
fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref,
@@ -5527,3 +5550,13 @@ bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) {
return a == b;
}
+ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
+ Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name));
+ resolve_top_level_decl(codegen, tld, false, nullptr);
+ assert(tld->id == TldIdVar);
+ TldVar *tld_var = (TldVar *)tld;
+ ConstExprValue *var_value = tld_var->var->value;
+ assert(var_value != nullptr);
+ return var_value;
+}
+
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 6224e64dd54551acd73435bc5de7face34aa0246..050630bc075ca134e05ae9603624ee7250e221b7 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -185,4 +185,8 @@ PackageTableEntry *new_anonymous_package(void);
Buf *const_value_to_buffer(ConstExprValue *const_val);
void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc);
+
+ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name);
+
+
#endif
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 9ad71a936effa38a48ce41a04c5a990d5aa89bd7..4de0fcec519983591ca8af9ca10c79b5d63cc152 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -483,7 +483,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
LLVMSetUnnamedAddr(fn_table_entry->llvm_value, true);
}
- if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) {
+ TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
+ if (return_type->id == TypeTableEntryIdUnreachable) {
addLLVMFnAttr(fn_table_entry->llvm_value, "noreturn");
}
@@ -520,13 +521,11 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
// use the ABI alignment, which is fine.
}
- if (!type_has_bits(fn_type->data.fn.fn_type_id.return_type)) {
+ if (!type_has_bits(return_type)) {
// nothing to do
- } else if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdPointer ||
- fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdFn)
- {
+ } else if (return_type->id == TypeTableEntryIdPointer || return_type->id == TypeTableEntryIdFn) {
addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");
- } else if (handle_is_ptr(fn_type->data.fn.fn_type_id.return_type) &&
+ } else if (handle_is_ptr(return_type) &&
calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc))
{
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
@@ -562,6 +561,10 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "byval");
}
}
+ if (return_type->id == TypeTableEntryIdErrorUnion || return_type->id == TypeTableEntryIdPureError) {
+ unsigned gen_index = LLVMCountParamTypes(fn_llvm_type) - 1;
+ addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull");
+ }
return fn_table_entry->llvm_value;
}
@@ -2330,7 +2333,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
TypeTableEntry *src_return_type = fn_type_id->return_type;
bool ret_has_bits = type_has_bits(src_return_type);
bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
- size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0);
+ bool last_arg_err_ret_stack = src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError;
+ size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (last_arg_err_ret_stack ? 1 : 0);
bool is_var_args = fn_type_id->is_var_args;
LLVMValueRef *gen_param_values = allocate(actual_param_count);
size_t gen_param_index = 0;
@@ -2348,6 +2352,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
gen_param_index += 1;
}
}
+ if (last_arg_err_ret_stack) {
+ gen_param_values[gen_param_index] = LLVMGetUndef(g->ptr_to_stack_trace_type->type_ref);
+ gen_param_index += 1;
+ }
ZigLLVM_FnInline fn_inline;
switch (instruction->fn_inline) {
@@ -5088,6 +5096,13 @@ static void define_builtin_compile_vars(CodeGen *g) {
os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
Buf *contents = buf_alloc();
+ buf_append_str(contents,
+ "pub const StackTrace = struct {\n"
+ " index: usize,\n"
+ " instruction_addresses: [31]usize,\n"
+ "};\n\n"
+ );
+
const char *cur_os = nullptr;
{
buf_appendf(contents, "pub const Os = enum {\n");
diff --git a/src/ir.cpp b/src/ir.cpp
index f236910250056995a4341aa3f0dc72db2cf48164..340e4fbeda2b80ed5ba5aa6c92982df4c39c9363 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -8230,16 +8230,6 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstruction *value, bool *out)
return ir_resolve_bool(ira, value, out);
}
-static ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
- Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name));
- resolve_top_level_decl(codegen, tld, false, nullptr);
- assert(tld->id == TldIdVar);
- TldVar *tld_var = (TldVar *)tld;
- ConstExprValue *var_value = tld_var->var->value;
- assert(var_value != nullptr);
- return var_value;
-}
-
static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) {
if (type_is_invalid(value->value.type))
return false;
--
2.54.0
From 32ea6f54e5f05c4173828c4f4c8ab9965a929120 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 12 Jan 2018 02:12:11 -0500
Subject: [PATCH 02/25] *WIP* proof of concept error return traces
---
build.zig | 12 ++--
src/all_types.hpp | 2 +
src/analyze.cpp | 6 +-
src/analyze.hpp | 1 +
src/codegen.cpp | 106 ++++++++++++++++++++++++----
src/ir.cpp | 12 ++++
std/build.zig | 6 +-
std/debug/index.zig | 110 ++++++++++++++++++++++++++++--
std/special/build_runner.zig | 4 +-
std/special/builtin.zig | 2 +-
std/special/compiler_rt/index.zig | 2 +-
std/special/panic.zig | 8 ++-
12 files changed, 238 insertions(+), 33 deletions(-)
diff --git a/build.zig b/build.zig
index 7c1563c7067949f68f64a16d346a29e2432ae99a..1fa984dcff585e6455c5615caa5293beea04d41b 100644
--- a/build.zig
+++ b/build.zig
@@ -10,7 +10,7 @@ const ArrayList = std.ArrayList;
const Buffer = std.Buffer;
const io = std.io;
-pub fn build(b: &Builder) {
+pub fn build(b: &Builder) -> %void {
const mode = b.standardReleaseOptions();
var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
@@ -36,7 +36,7 @@ pub fn build(b: &Builder) {
const test_step = b.step("test", "Run all the tests");
// find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library
- const build_info = b.exec([][]const u8{b.zig_exe, "BUILD_INFO"});
+ const build_info = try b.exec([][]const u8{b.zig_exe, "BUILD_INFO"});
var index: usize = 0;
const cmake_binary_dir = nextValue(&index, build_info);
const cxx_compiler = nextValue(&index, build_info);
@@ -68,7 +68,7 @@ pub fn build(b: &Builder) {
dependOnLib(exe, llvm);
if (exe.target.getOs() == builtin.Os.linux) {
- const libstdcxx_path_padded = b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"});
+ const libstdcxx_path_padded = try b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"});
const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next();
exe.addObjectFile(libstdcxx_path);
@@ -155,9 +155,9 @@ const LibraryDep = struct {
};
fn findLLVM(b: &Builder, llvm_config_exe: []const u8) -> %LibraryDep {
- const libs_output = b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});
- const includes_output = b.exec([][]const u8{llvm_config_exe, "--includedir"});
- const libdir_output = b.exec([][]const u8{llvm_config_exe, "--libdir"});
+ const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});
+ const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"});
+ const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"});
var result = LibraryDep {
.libs = ArrayList([]const u8).init(b.allocator),
diff --git a/src/all_types.hpp b/src/all_types.hpp
index caae13d20d4a495146ab7f66308f350fff07d48f..6476c08625f12f2ed59538435c2f338334df452b 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1205,6 +1205,7 @@ struct FnTableEntry {
uint32_t alignstack_value;
ZigList export_list;
+ bool calls_errorable_function;
};
uint32_t fn_table_entry_hash(FnTableEntry*);
@@ -1530,6 +1531,7 @@ struct CodeGen {
FnTableEntry *panic_fn;
LLVMValueRef cur_ret_ptr;
LLVMValueRef cur_fn_val;
+ LLVMValueRef cur_err_ret_trace_val;
bool c_want_stdint;
bool c_want_stdbool;
AstNode *root_export_decl;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 2e51e5151cae89d7ecc1d560b999820c49dc27c5..61d86b5eb2a9cd05971a4566aaf61eb0da01556d 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -869,7 +869,7 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) {
zig_unreachable();
}
-static TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) {
+TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) {
if (g->stack_trace_type == nullptr) {
ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace");
assert(stack_trace_type_val->type->id == TypeTableEntryIdMetaType);
@@ -1191,6 +1191,9 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
}
TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
+ if (type_is_invalid(type_entry)) {
+ return g->builtin_types.entry_invalid;
+ }
if (fn_type_id.cc != CallingConventionUnspecified) {
type_ensure_zero_bits_known(g, type_entry);
if (!type_has_bits(type_entry)) {
@@ -2586,6 +2589,7 @@ static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntr
}
static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
+ return; // TODO
AstNode *proto_node = panic_fn->proto_node;
assert(proto_node->type == NodeTypeFnProto);
TypeTableEntry *fn_type = panic_fn->type_entry;
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 050630bc075ca134e05ae9603624ee7250e221b7..3992cefdfcd6daa5bee3eb8d308817c31e0db849 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -187,6 +187,7 @@ void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, G
ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name);
+TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g);
#endif
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 4de0fcec519983591ca8af9ca10c79b5d63cc152..97cab9a69f53b8dfd457c2339e71001c95de228f 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -22,6 +22,8 @@
#include
#include
+static const size_t stack_trace_ptr_count = 31;
+
static void init_darwin_native(CodeGen *g) {
char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET");
char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET");
@@ -867,16 +869,24 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) {
return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0));
}
-static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) {
+static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace_arg) {
assert(g->panic_fn != nullptr);
LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn);
LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc);
- ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, ZigLLVM_FnInlineAuto, "");
+ if (stack_trace_arg == nullptr) {
+ TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
+ stack_trace_arg = LLVMConstNull(ptr_to_stack_trace_type->type_ref);
+ }
+ LLVMValueRef args[] = {
+ msg_arg,
+ stack_trace_arg,
+ };
+ ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, "");
LLVMBuildUnreachable(g->builder);
}
static void gen_debug_safety_crash(CodeGen *g, PanicMsgId msg_id) {
- gen_panic(g, get_panic_msg_ptr_val(g, msg_id));
+ gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr);
}
static LLVMValueRef get_memcpy_fn_val(CodeGen *g) {
@@ -956,7 +966,11 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
LLVMValueRef offset_buf_ptr = LLVMConstInBoundsGEP(global_array, offset_ptr_indices, 2);
Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false);
- LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), &g->err_tag_type->type_ref, 1, false);
+ LLVMTypeRef arg_types[] = {
+ g->err_tag_type->type_ref,
+ g->ptr_to_stack_trace_type->type_ref,
+ };
+ LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
addLLVMFnAttr(fn_val, "noreturn");
addLLVMFnAttr(fn_val, "cold");
@@ -1008,7 +1022,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
LLVMValueRef global_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, global_slice, slice_len_index, "");
gen_store(g, full_buf_len, global_slice_len_field_ptr, u8_ptr_type);
- gen_panic(g, global_slice);
+ gen_panic(g, global_slice, LLVMGetParam(fn_val, 1));
LLVMPositionBuilderAtEnd(g->builder, prev_block);
LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
@@ -1019,7 +1033,16 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
- ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified),
+ LLVMValueRef err_ret_trace_val = g->cur_err_ret_trace_val;
+ if (err_ret_trace_val == nullptr) {
+ TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
+ err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref);
+ }
+ LLVMValueRef args[] = {
+ err_val,
+ err_ret_trace_val,
+ };
+ ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified),
ZigLLVM_FnInlineAuto, "");
LLVMBuildUnreachable(g->builder);
}
@@ -1299,6 +1322,50 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) {
LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
TypeTableEntry *return_type = return_instruction->value->value.type;
+
+ bool is_err_return = false;
+ if (return_type->id == TypeTableEntryIdErrorUnion) {
+ if (return_instruction->value->value.special == ConstValSpecialStatic) {
+ is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr;
+ } else if (return_instruction->value->value.special == ConstValSpecialRuntime) {
+ is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
+ // TODO: emit a branch to check if the return value is an error
+ }
+ } else if (return_type->id == TypeTableEntryIdPureError) {
+ is_err_return = true;
+ }
+ if (is_err_return) {
+ LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
+
+ // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = @instructionPointer();
+ // stack_trace.index += 1;
+
+ LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError");
+
+ LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block);
+ size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
+ LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, "");
+ size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
+ LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, "");
+
+ // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = @instructionPointer();
+ LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, "");
+ LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), "");
+ LLVMValueRef address_indices[] = {
+ LLVMConstNull(usize_type_ref),
+ modded_val,
+ };
+ LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, addresses_field_ptr, address_indices, 2, "");
+ LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, block_address, usize_type_ref, "");
+ gen_store_untyped(g, address_value, address_slot, 0, false);
+
+ // stack_trace.index += 1;
+ LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), "");
+ gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false);
+
+ LLVMBuildBr(g->builder, return_block);
+ LLVMPositionBuilderAtEnd(g->builder, return_block);
+ }
if (handle_is_ptr(return_type)) {
if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) {
assert(g->cur_ret_ptr);
@@ -2353,7 +2420,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
}
}
if (last_arg_err_ret_stack) {
- gen_param_values[gen_param_index] = LLVMGetUndef(g->ptr_to_stack_trace_type->type_ref);
+ gen_param_values[gen_param_index] = g->cur_err_ret_trace_val;
gen_param_index += 1;
}
@@ -3482,7 +3549,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec
}
static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) {
- gen_panic(g, ir_llvm_value(g, instruction->msg));
+ gen_panic(g, ir_llvm_value(g, instruction->msg), nullptr);
return nullptr;
}
@@ -4501,7 +4568,8 @@ static void do_code_gen(CodeGen *g) {
LLVMValueRef fn = fn_llvm_value(g, fn_table_entry);
g->cur_fn = fn_table_entry;
g->cur_fn_val = fn;
- if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) {
+ TypeTableEntry *return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
+ if (handle_is_ptr(return_type)) {
g->cur_ret_ptr = LLVMGetParam(fn, 0);
} else {
g->cur_ret_ptr = nullptr;
@@ -4510,6 +4578,18 @@ static void do_code_gen(CodeGen *g) {
build_all_basic_blocks(g, fn_table_entry);
clear_debug_source_node(g);
+ if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) {
+ g->cur_err_ret_trace_val = LLVMGetParam(fn, LLVMCountParamTypes(fn_table_entry->type_entry->data.fn.raw_type_ref) - 1);
+ } else if (fn_table_entry->calls_errorable_function) {
+ g->cur_err_ret_trace_val = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type));
+ size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
+ LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, "");
+ TypeTableEntry *usize = g->builtin_types.entry_usize;
+ gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false);
+ } else {
+ g->cur_err_ret_trace_val = nullptr;
+ }
+
// allocate temporary stack data
for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) {
IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i);
@@ -5096,12 +5176,11 @@ static void define_builtin_compile_vars(CodeGen *g) {
os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
Buf *contents = buf_alloc();
- buf_append_str(contents,
+ buf_appendf(contents,
"pub const StackTrace = struct {\n"
" index: usize,\n"
- " instruction_addresses: [31]usize,\n"
- "};\n\n"
- );
+ " instruction_addresses: [%" ZIG_PRI_usize "]usize,\n"
+ "};\n\n", stack_trace_ptr_count);
const char *cur_os = nullptr;
{
@@ -5266,6 +5345,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
g->compile_var_import = add_source_file(g, g->compile_var_package, abs_full_path, contents);
+ scan_import(g, g->compile_var_import);
}
static void init(CodeGen *g) {
diff --git a/src/ir.cpp b/src/ir.cpp
index 340e4fbeda2b80ed5ba5aa6c92982df4c39c9363..5dd608083f59e7a2af5e8c21753f8e4e0352f3a3 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -10043,9 +10043,21 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
ir_add_alloca(ira, new_call_instruction, return_type);
+ if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) {
+ parent_fn_entry->calls_errorable_function = true;
+ }
+
return ir_finish_anal(ira, return_type);
}
+ FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
+ assert(fn_type_id->return_type != nullptr);
+ assert(parent_fn_entry != nullptr);
+ if (fn_type_id->return_type->id == TypeTableEntryIdPureError || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) {
+ parent_fn_entry->calls_errorable_function = true;
+ }
+
+
IrInstruction **casted_args = allocate(call_param_count);
size_t next_arg_index = 0;
if (first_arg_ptr) {
diff --git a/std/build.zig b/std/build.zig
index cd6b3811eade0e95c7046541da4c54c538288b5c..6984715466453c4b837f8495da116833a7920e24 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -721,11 +721,9 @@ pub const Builder = struct {
return error.FileNotFound;
}
- pub fn exec(self: &Builder, argv: []const []const u8) -> []u8 {
+ pub fn exec(self: &Builder, argv: []const []const u8) -> %[]u8 {
const max_output_size = 100 * 1024;
- const result = os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size) catch |err| {
- std.debug.panic("Unable to spawn {}: {}", argv[0], @errorName(err));
- };
+ const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size);
switch (result.term) {
os.ChildProcess.Term.Exited => |code| {
if (code != 0) {
diff --git a/std/debug/index.zig b/std/debug/index.zig
index 464974b7debe2a16bd2dcb2566693e384b18e474..fa2304079404c64d21e9fbcf6c7741d5bbf67549 100644
--- a/std/debug/index.zig
+++ b/std/debug/index.zig
@@ -37,10 +37,16 @@ fn getStderrStream() -> %&io.OutStream {
}
}
+/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
+pub fn dumpCurrentStackTrace() {
+ const stderr = getStderrStream() catch return;
+ writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return;
+}
+
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
-pub fn dumpStackTrace() {
+pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) {
const stderr = getStderrStream() catch return;
- writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return;
+ writeStackTrace(stack_trace, stderr, global_allocator, stderr_file.isTty()) catch return;
}
/// This function invokes undefined behavior when `ok` is `false`.
@@ -88,7 +94,7 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
const stderr = getStderrStream() catch os.abort();
stderr.print(format ++ "\n", args) catch os.abort();
- writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort();
+ writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort();
os.abort();
}
@@ -101,7 +107,103 @@ const RESET = "\x1b[0m";
error PathNotFound;
error InvalidDebugInfo;
-pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool,
+pub fn writeStackTrace(st_addrs: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool) -> %void {
+ switch (builtin.object_format) {
+ builtin.ObjectFormat.elf => {
+ var stack_trace = ElfStackTrace {
+ .self_exe_file = undefined,
+ .elf = undefined,
+ .debug_info = undefined,
+ .debug_abbrev = undefined,
+ .debug_str = undefined,
+ .debug_line = undefined,
+ .debug_ranges = null,
+ .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
+ .compile_unit_list = ArrayList(CompileUnit).init(allocator),
+ };
+ const st = &stack_trace;
+ st.self_exe_file = try os.openSelfExe();
+ defer st.self_exe_file.close();
+
+ try st.elf.openFile(allocator, &st.self_exe_file);
+ defer st.elf.close();
+
+ st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
+ st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
+ st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo;
+ st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
+ st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
+ try scanAllCompileUnits(st);
+
+ var ignored_count: usize = 0;
+
+ var frame_index: usize = undefined;
+ var frames_left: usize = undefined;
+ if (st_addrs.index < st_addrs.instruction_addresses.len) {
+ frame_index = 0;
+ frames_left = st_addrs.index;
+ } else {
+ frame_index = (st_addrs.index + 1) % st_addrs.instruction_addresses.len;
+ frames_left = st_addrs.instruction_addresses.len;
+ }
+
+ while (frames_left != 0) : ({frames_left -= 1; frame_index = (frame_index + 1) % st_addrs.instruction_addresses.len;}) {
+ const return_address = st_addrs.instruction_addresses[frame_index];
+
+ // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
+ // at compile time. I'll call it issue #313
+ const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
+
+ const compile_unit = findCompileUnit(st, return_address) catch {
+ try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
+ return_address);
+ continue;
+ };
+ const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name);
+ if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| {
+ defer line_info.deinit();
+ try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
+ DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
+ line_info.file_name, line_info.line, line_info.column,
+ return_address, compile_unit_name);
+ if (printLineFromFile(st.allocator(), out_stream, line_info)) {
+ if (line_info.column == 0) {
+ try out_stream.write("\n");
+ } else {
+ {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
+ try out_stream.writeByte(' ');
+ }}
+ try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
+ }
+ } else |err| switch (err) {
+ error.EndOfFile, error.PathNotFound => {},
+ else => return err,
+ }
+ } else |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => {
+ try out_stream.print(ptr_hex ++ " in ??? ({})\n",
+ return_address, compile_unit_name);
+ },
+ else => return err,
+ }
+ }
+ },
+ builtin.ObjectFormat.coff => {
+ try out_stream.write("(stack trace unavailable for COFF object format)\n");
+ },
+ builtin.ObjectFormat.macho => {
+ try out_stream.write("(stack trace unavailable for Mach-O object format)\n");
+ },
+ builtin.ObjectFormat.wasm => {
+ try out_stream.write("(stack trace unavailable for WASM object format)\n");
+ },
+ builtin.ObjectFormat.unknown => {
+ try out_stream.write("(stack trace unavailable for unknown object format)\n");
+ },
+ }
+}
+
+pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool,
ignore_frame_count: usize) -> %void
{
switch (builtin.object_format) {
diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig
index 24d9c756e7c633a186a34a44fb04c3391d3d3df2..b293a1e64db7d30756d985e02a595bbb53b8b33e 100644
--- a/std/special/build_runner.zig
+++ b/std/special/build_runner.zig
@@ -112,7 +112,7 @@ pub fn main() -> %void {
}
builder.setInstallPrefix(prefix);
- root.build(&builder);
+ root.build(&builder) catch unreachable;
if (builder.validateUserInputDidItFail())
return usageAndErr(&builder, true, try stderr_stream);
@@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
// run the build script to collect the options
if (!already_ran_build) {
builder.setInstallPrefix(null);
- root.build(builder);
+ root.build(builder) catch unreachable;
}
// This usage text has to be synchronized with src/main.cpp
diff --git a/std/special/builtin.zig b/std/special/builtin.zig
index e6c09863ca98620920fb40679ec788905e0298f6..dd77ba9c75c805618a96fe018167aef9f7a70bac 100644
--- a/std/special/builtin.zig
+++ b/std/special/builtin.zig
@@ -5,7 +5,7 @@ const builtin = @import("builtin");
// Avoid dragging in the debug safety mechanisms into this .o file,
// unless we're trying to test this file.
-pub coldcc fn panic(msg: []const u8) -> noreturn {
+pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
if (builtin.is_test) {
@import("std").debug.panic("{}", msg);
} else {
diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig
index 5034a6fd906608e9c3d98298430211e7ddd8fd7e..96f34e56ff64889e2f126f6d4bfb361b5abfb9ad 100644
--- a/std/special/compiler_rt/index.zig
+++ b/std/special/compiler_rt/index.zig
@@ -74,7 +74,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;
// Avoid dragging in the debug safety mechanisms into this .o file,
// unless we're trying to test this file.
-pub coldcc fn panic(msg: []const u8) -> noreturn {
+pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
if (is_test) {
@import("std").debug.panic("{}", msg);
} else {
diff --git a/std/special/panic.zig b/std/special/panic.zig
index 03c2586739f85f8bad99e879bfca38faafe5cd29..690c4afa1c814d5f4718370ad111548160d4e809 100644
--- a/std/special/panic.zig
+++ b/std/special/panic.zig
@@ -4,14 +4,20 @@
// have to be added in the compiler.
const builtin = @import("builtin");
+const std = @import("std");
-pub coldcc fn panic(msg: []const u8) -> noreturn {
+pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
switch (builtin.os) {
// TODO: fix panic in zen.
builtin.Os.freestanding, builtin.Os.zen => {
while (true) {}
},
else => {
+ if (error_return_trace) |trace| {
+ std.debug.warn("{}\n", msg);
+ std.debug.dumpStackTrace(trace);
+ @import("std").debug.panic("");
+ }
@import("std").debug.panic("{}", msg);
},
}
--
2.54.0
From 304f6f1d0165f9bdacb7d80479298ca0acff1c27 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Sat, 13 Jan 2018 13:23:12 +1300
Subject: [PATCH 03/25] Add integer rotation functions
---
std/math/index.zig | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/std/math/index.zig b/std/math/index.zig
index 081c59a88aa051f2efe3f3a01db983274a3e3c85..d925740030df1c092f5e60768b0fbe5342f4631b 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -267,6 +267,45 @@ test "math.shr" {
assert(shr(u8, 0b11111111, isize(-2)) == 0b11111100);
}
+/// Rotates right. Only unsigned values can be rotated.
+/// Negative shift values results in shift modulo the bit count.
+pub fn rotr(comptime T: type, x: T, r: var) -> T {
+ if (T.is_signed) {
+ @compileError("cannot rotate signed integer");
+ } else {
+ const ar = @mod(r, T.bit_count);
+ return shr(T, x, ar) | shl(T, x, T.bit_count - ar);
+ }
+}
+
+test "math.rotr" {
+ assert(rotr(u8, 0b00000001, usize(0)) == 0b00000001);
+ assert(rotr(u8, 0b00000001, usize(9)) == 0b10000000);
+ assert(rotr(u8, 0b00000001, usize(8)) == 0b00000001);
+ assert(rotr(u8, 0b00000001, usize(4)) == 0b00010000);
+ assert(rotr(u8, 0b00000001, isize(-1)) == 0b00000010);
+}
+
+/// Rotates left. Only unsigned values can be rotated.
+/// Negative shift values results in shift modulo the bit count.
+pub fn rotl(comptime T: type, x: T, r: var) -> T {
+ if (T.is_signed) {
+ @compileError("cannot rotate signed integer");
+ } else {
+ const ar = @mod(r, T.bit_count);
+ return shl(T, x, ar) | shr(T, x, T.bit_count - ar);
+ }
+}
+
+test "math.rotl" {
+ assert(rotl(u8, 0b00000001, usize(0)) == 0b00000001);
+ assert(rotl(u8, 0b00000001, usize(9)) == 0b00000010);
+ assert(rotl(u8, 0b00000001, usize(8)) == 0b00000001);
+ assert(rotl(u8, 0b00000001, usize(4)) == 0b00010000);
+ assert(rotl(u8, 0b00000001, isize(-1)) == 0b10000000);
+}
+
+
pub fn Log2Int(comptime T: type) -> type {
return @IntType(false, log2(T.bit_count));
}
--
2.54.0
From 51fdbf7f8c282c7b91a688c9069b363e90bedf6e Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Sat, 13 Jan 2018 14:40:21 +1300
Subject: [PATCH 04/25] Add Md5 and Sha1 hash functions
Some performance comparisons to C.
We take the fastest time measurement taken across multiple runs.
The block hashing functions use the same md5/sha1 methods.
```
Cpu: Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz
Gcc: 7.2.1 20171224
Clang: 5.0.1
Zig: 0.1.1.304f6f1d
```
See https://www.nayuki.io/page/fast-md5-hash-implementation-in-x86-assembly:
```
gcc -O2
661 Mb/s
clang -O2
490 Mb/s
zig --release-fast and zig --release-safe
570 Mb/s
zig
50 Mb/s
```
See https://www.nayuki.io/page/fast-sha1-hash-implementation-in-x86-assembly:
```
gcc -O2
588 Mb/s
clang -O2
563 Mb/s
zig --release-fast and zig --release-safe
610 Mb/s
zig
21 Mb/s
```
In short, zig provides pretty useful tools for writing this sort of
code. We are in the lead against clang (which uses the same LLVM
backend) with us being slower only against md5 with GCC.
---
CMakeLists.txt | 3 +
std/crypto/index.zig | 7 ++
std/crypto/md5.zig | 252 ++++++++++++++++++++++++++++++++++++++
std/crypto/sha1.zig | 280 +++++++++++++++++++++++++++++++++++++++++++
std/index.zig | 2 +
5 files changed, 544 insertions(+)
create mode 100644 std/crypto/index.zig
create mode 100644 std/crypto/md5.zig
create mode 100644 std/crypto/sha1.zig
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 252b07e6ddae0a766bd1ad35d4983f35d5a662e7..1e9691f1c620beef18f84d4fe58e42f278dc8f22 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -364,6 +364,9 @@ set(ZIG_STD_FILES
"c/index.zig"
"c/linux.zig"
"c/windows.zig"
+ "crypto/index.zig"
+ "crypto/md5.zig"
+ "crypto/sha1.zig"
"cstr.zig"
"debug/failing_allocator.zig"
"debug/index.zig"
diff --git a/std/crypto/index.zig b/std/crypto/index.zig
new file mode 100644
index 0000000000000000000000000000000000000000..eaa371555a626ffa188aa1eabac66165ae248683
--- /dev/null
+++ b/std/crypto/index.zig
@@ -0,0 +1,7 @@
+pub const Sha1 = @import("md5.zig").Sha1;
+pub const Md5 = @import("sha1.zig").Md5;
+
+test "crypto" {
+ _ = @import("md5.zig");
+ _ = @import("sha1.zig");
+}
diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig
new file mode 100644
index 0000000000000000000000000000000000000000..a324e4b8c6ff2d4cf8a8c2b0d2f6485506fd6111
--- /dev/null
+++ b/std/crypto/md5.zig
@@ -0,0 +1,252 @@
+const mem = @import("../mem.zig");
+const math = @import("../math/index.zig");
+const endian = @import("../endian.zig");
+const debug = @import("../debug/index.zig");
+
+const RoundParam = struct {
+ a: u32, b: u32, c: u32, d: u32,
+ k: u32, s: u32, t: u32
+};
+
+fn Rp(a: u32, b: u32, c: u32, d: u32, k: u32, s: u5, t: u32) -> RoundParam {
+ return RoundParam { .a = a, .b = b, .c = c, .d = d, .k = k, .s = s, .t = t };
+}
+
+/// const hash1 = Md5.hash("my input");
+///
+/// const hasher = Md5.init();
+/// hasher.update("my ");
+/// hasher.update("input");
+/// const hash2 = hasher.final();
+pub const Md5 = struct {
+ const Self = this;
+
+ s: [4]u32,
+ // Streaming Cache
+ buf: [64]u8,
+ buf_len: u8,
+ total_len: u64,
+
+ pub fn init() -> Self {
+ var d: Self = undefined;
+ d.reset();
+ return d;
+ }
+
+ pub fn reset(d: &Self) {
+ d.s[0] = 0x67452301;
+ d.s[1] = 0xEFCDAB89;
+ d.s[2] = 0x98BADCFE;
+ d.s[3] = 0x10325476;
+ d.buf_len = 0;
+ d.total_len = 0;
+ }
+
+ pub fn hash(b: []const u8) -> u128 {
+ var d = Md5.init();
+ d.update(b);
+ return d.final();
+ }
+
+ pub fn update(d: &Self, b: []const u8) {
+ var off: usize = 0;
+
+ // Partial buffer exists from previous update. Copy into buffer then hash.
+ if (d.buf_len != 0 and d.buf_len + b.len > 64) {
+ off += 64 - d.buf_len;
+ mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
+
+ d.round(d.buf[0..]);
+ d.buf_len = 0;
+ }
+
+ // Full middle blocks.
+ while (off + 64 < b.len) : (off += 64) {
+ d.round(b[off..off + 64]);
+ }
+
+ // Copy any remainder for next pass.
+ mem.copy(u8, d.buf[d.buf_len..], b[off..]);
+ d.buf_len += u8(b[off..].len);
+
+ d.total_len += b.len;
+ }
+
+ pub fn final(d: &Self) -> u128 {
+ // The buffer here will never be completely full.
+ mem.set(u8, d.buf[d.buf_len..], 0);
+
+ // Append padding bits.
+ d.buf[d.buf_len] = 0x80;
+ d.buf_len += 1;
+
+ // > 448 mod 512 so need to add an extra round to wrap around.
+ if (64 - d.buf_len < 8) {
+ d.round(d.buf[0..]);
+ mem.set(u8, d.buf[0..], 0);
+ }
+
+ // Append message length.
+ var i: usize = 1;
+ var len = d.total_len >> 5;
+ d.buf[56] = u8(d.total_len & 0x1f) << 3;
+ while (i < 8) : (i += 1) {
+ d.buf[56 + i] = u8(len & 0xff);
+ len >>= 8;
+ }
+
+ d.round(d.buf[0..]);
+
+ const r =
+ (u128(d.s[3]) << 96) |
+ (u128(d.s[2]) << 64) |
+ (u128(d.s[1]) << 32) |
+ (u128(d.s[0]) << 0);
+
+ return endian.swapIfLe(u128, r);
+ }
+
+ fn round(d: &Self, b: []const u8) {
+ debug.assert(b.len == 64);
+
+ var s: [16]u32 = undefined;
+
+ // ERROR: cannot unroll this at comptime
+ var i: usize = 0;
+ while (i < 16) : (i += 1) {
+ // NOTE: Performing or's separately improves perf by ~10%
+ s[i] = 0;
+ s[i] |= u32(b[i*4+0]);
+ s[i] |= u32(b[i*4+1]) << 8;
+ s[i] |= u32(b[i*4+2]) << 16;
+ s[i] |= u32(b[i*4+3]) << 24;
+ }
+
+ var v: [4]u32 = []u32 {
+ d.s[0], d.s[1], d.s[2], d.s[3],
+ };
+
+ const round0 = comptime []RoundParam {
+ Rp(0, 1, 2, 3, 0, 7, 0xD76AA478),
+ Rp(3, 0, 1, 2, 1, 12, 0xE8C7B756),
+ Rp(2, 3, 0, 1, 2, 17, 0x242070DB),
+ Rp(1, 2, 3, 0, 3, 22, 0xC1BDCEEE),
+ Rp(0, 1, 2, 3, 4, 7, 0xF57C0FAF),
+ Rp(3, 0, 1, 2, 5, 12, 0x4787C62A),
+ Rp(2, 3, 0, 1, 6, 17, 0xA8304613),
+ Rp(1, 2, 3, 0, 7, 22, 0xFD469501),
+ Rp(0, 1, 2, 3, 8, 7, 0x698098D8),
+ Rp(3, 0, 1, 2, 9, 12, 0x8B44F7AF),
+ Rp(2, 3, 0, 1, 10, 17, 0xFFFF5BB1),
+ Rp(1, 2, 3, 0, 11, 22, 0x895CD7BE),
+ Rp(0, 1, 2, 3, 12, 7, 0x6B901122),
+ Rp(3, 0, 1, 2, 13, 12, 0xFD987193),
+ Rp(2, 3, 0, 1, 14, 17, 0xA679438E),
+ Rp(1, 2, 3, 0, 15, 22, 0x49B40821),
+ };
+ inline for (round0) |r| {
+ v[r.a] = v[r.a] +% (v[r.d] ^ (v[r.b] & (v[r.c] ^ v[r.d]))) +% r.t +% s[r.k];
+ v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s);
+ }
+
+ const round1 = comptime []RoundParam {
+ Rp(0, 1, 2, 3, 1, 5, 0xF61E2562),
+ Rp(3, 0, 1, 2, 6, 9, 0xC040B340),
+ Rp(2, 3, 0, 1, 11, 14, 0x265E5A51),
+ Rp(1, 2, 3, 0, 0, 20, 0xE9B6C7AA),
+ Rp(0, 1, 2, 3, 5, 5, 0xD62F105D),
+ Rp(3, 0, 1, 2, 10, 9, 0x02441453),
+ Rp(2, 3, 0, 1, 15, 14, 0xD8A1E681),
+ Rp(1, 2, 3, 0, 4, 20, 0xE7D3FBC8),
+ Rp(0, 1, 2, 3, 9, 5, 0x21E1CDE6),
+ Rp(3, 0, 1, 2, 14, 9, 0xC33707D6),
+ Rp(2, 3, 0, 1, 3, 14, 0xF4D50D87),
+ Rp(1, 2, 3, 0, 8, 20, 0x455A14ED),
+ Rp(0, 1, 2, 3, 13, 5, 0xA9E3E905),
+ Rp(3, 0, 1, 2, 2, 9, 0xFCEFA3F8),
+ Rp(2, 3, 0, 1, 7, 14, 0x676F02D9),
+ Rp(1, 2, 3, 0, 12, 20, 0x8D2A4C8A),
+ };
+ inline for (round1) |r| {
+ v[r.a] = v[r.a] +% (v[r.c] ^ (v[r.d] & (v[r.b] ^ v[r.c]))) +% r.t +% s[r.k];
+ v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s);
+ }
+
+ const round2 = comptime []RoundParam {
+ Rp(0, 1, 2, 3, 5, 4, 0xFFFA3942),
+ Rp(3, 0, 1, 2, 8, 11, 0x8771F681),
+ Rp(2, 3, 0, 1, 11, 16, 0x6D9D6122),
+ Rp(1, 2, 3, 0, 14, 23, 0xFDE5380C),
+ Rp(0, 1, 2, 3, 1, 4, 0xA4BEEA44),
+ Rp(3, 0, 1, 2, 4, 11, 0x4BDECFA9),
+ Rp(2, 3, 0, 1, 7, 16, 0xF6BB4B60),
+ Rp(1, 2, 3, 0, 10, 23, 0xBEBFBC70),
+ Rp(0, 1, 2, 3, 13, 4, 0x289B7EC6),
+ Rp(3, 0, 1, 2, 0, 11, 0xEAA127FA),
+ Rp(2, 3, 0, 1, 3, 16, 0xD4EF3085),
+ Rp(1, 2, 3, 0, 6, 23, 0x04881D05),
+ Rp(0, 1, 2, 3, 9, 4, 0xD9D4D039),
+ Rp(3, 0, 1, 2, 12, 11, 0xE6DB99E5),
+ Rp(2, 3, 0, 1, 15, 16, 0x1FA27CF8),
+ Rp(1, 2, 3, 0, 2, 23, 0xC4AC5665),
+ };
+ inline for (round2) |r| {
+ v[r.a] = v[r.a] +% (v[r.b] ^ v[r.c] ^ v[r.d]) +% r.t +% s[r.k];
+ v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s);
+ }
+
+ const round3 = comptime []RoundParam {
+ Rp(0, 1, 2, 3, 0, 6, 0xF4292244),
+ Rp(3, 0, 1, 2, 7, 10, 0x432AFF97),
+ Rp(2, 3, 0, 1, 14, 15, 0xAB9423A7),
+ Rp(1, 2, 3, 0, 5, 21, 0xFC93A039),
+ Rp(0, 1, 2, 3, 12, 6, 0x655B59C3),
+ Rp(3, 0, 1, 2, 3, 10, 0x8F0CCC92),
+ Rp(2, 3, 0, 1, 10, 15, 0xFFEFF47D),
+ Rp(1, 2, 3, 0, 1, 21, 0x85845DD1),
+ Rp(0, 1, 2, 3, 8, 6, 0x6FA87E4F),
+ Rp(3, 0, 1, 2, 15, 10, 0xFE2CE6E0),
+ Rp(2, 3, 0, 1, 6, 15, 0xA3014314),
+ Rp(1, 2, 3, 0, 13, 21, 0x4E0811A1),
+ Rp(0, 1, 2, 3, 4, 6, 0xF7537E82),
+ Rp(3, 0, 1, 2, 11, 10, 0xBD3AF235),
+ Rp(2, 3, 0, 1, 2, 15, 0x2AD7D2BB),
+ Rp(1, 2, 3, 0, 9, 21, 0xEB86D391),
+ };
+ inline for (round3) |r| {
+ v[r.a] = v[r.a] +% (v[r.c] ^ (v[r.b] | ~v[r.d])) +% r.t +% s[r.k];
+ v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s);
+ }
+
+ d.s[0] +%= v[0];
+ d.s[1] +%= v[1];
+ d.s[2] +%= v[2];
+ d.s[3] +%= v[3];
+ }
+};
+
+test "md5 single" {
+ debug.assert(0xd41d8cd98f00b204e9800998ecf8427e == Md5.hash(""));
+ debug.assert(0x0cc175b9c0f1b6a831c399e269772661 == Md5.hash("a"));
+ debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == Md5.hash("abc"));
+ debug.assert(0xf96b697d7cb7938d525a2f31aaf161d0 == Md5.hash("message digest"));
+ debug.assert(0xc3fcd3d76192e4007dfb496cca67e13b == Md5.hash("abcdefghijklmnopqrstuvwxyz"));
+ debug.assert(0xd174ab98d277d9f5a5611c2c9f419d9f == Md5.hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"));
+ debug.assert(0x57edf4a22be3c955ac49da2e2107b67a == Md5.hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890"));
+}
+
+test "md5 streaming" {
+ var h = Md5.init();
+
+ debug.assert(0xd41d8cd98f00b204e9800998ecf8427e == h.final());
+
+ h.reset();
+ h.update("abc");
+ debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == h.final());
+
+ h.reset();
+ h.update("a");
+ h.update("b");
+ h.update("c");
+ debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == h.final());
+}
diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig
new file mode 100644
index 0000000000000000000000000000000000000000..6a6d8ba3eeb7528b630fe6aed5536d697dc565e3
--- /dev/null
+++ b/std/crypto/sha1.zig
@@ -0,0 +1,280 @@
+const mem = @import("../mem.zig");
+const math = @import("../math/index.zig");
+const endian = @import("../endian.zig");
+const debug = @import("../debug/index.zig");
+
+pub const u160 = @IntType(false, 160);
+
+const RoundParam = struct {
+ a: u32, b: u32, c: u32, d: u32, e: u32, i: u32,
+};
+
+fn Rp(a: u32, b: u32, c: u32, d: u32, e: u32, i: u32) -> RoundParam {
+ return RoundParam { .a = a, .b = b, .c = c, .d = d, .e = e, .i = i };
+}
+
+pub const Sha1 = struct {
+ const Self = this;
+
+ s: [5]u32,
+ // Streaming Cache
+ buf: [64]u8,
+ buf_len: u8,
+ total_len: u64,
+
+ pub fn init() -> Self {
+ var d: Self = undefined;
+ d.reset();
+ return d;
+ }
+
+ pub fn reset(d: &Self) {
+ d.s[0] = 0x67452301;
+ d.s[1] = 0xEFCDAB89;
+ d.s[2] = 0x98BADCFE;
+ d.s[3] = 0x10325476;
+ d.s[4] = 0xC3D2E1F0;
+ d.buf_len = 0;
+ d.total_len = 0;
+ }
+
+ pub fn hash(b: []const u8) -> u160 {
+ var d = Sha1.init();
+ d.update(b);
+ return d.final();
+ }
+
+ pub fn update(d: &Self, b: []const u8) {
+ var off: usize = 0;
+
+ // Partial buffer exists from previous update. Copy into buffer then hash.
+ if (d.buf_len != 0 and d.buf_len + b.len > 64) {
+ off += 64 - d.buf_len;
+ mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
+
+ d.round(d.buf[0..]);
+ d.buf_len = 0;
+ }
+
+ // Full middle blocks.
+ while (off + 64 < b.len) : (off += 64) {
+ d.round(b[off..off + 64]);
+ }
+
+ // Copy any remainder for next pass.
+ mem.copy(u8, d.buf[d.buf_len..], b[off..]);
+ d.buf_len += u8(b[off..].len);
+
+ d.total_len += b.len;
+ }
+
+ pub fn final(d: &Self) -> u160 {
+ // The buffer here will never be completely full.
+ mem.set(u8, d.buf[d.buf_len..], 0);
+
+ // Append padding bits.
+ d.buf[d.buf_len] = 0x80;
+ d.buf_len += 1;
+
+ // > 448 mod 512 so need to add an extra round to wrap around.
+ if (64 - d.buf_len < 8) {
+ d.round(d.buf[0..]);
+ mem.set(u8, d.buf[0..], 0);
+ }
+
+ // Append message length.
+ var i: usize = 1;
+ var len = d.total_len >> 5;
+ d.buf[63] = u8(d.total_len & 0x1f) << 3;
+ while (i < 8) : (i += 1) {
+ d.buf[63 - i] = u8(len & 0xff);
+ len >>= 8;
+ }
+
+ d.round(d.buf[0..]);
+
+ const r =
+ (u160(d.s[0]) << 128) |
+ (u160(d.s[1]) << 96) |
+ (u160(d.s[2]) << 64) |
+ (u160(d.s[3]) << 32) |
+ (u160(d.s[4]) << 0);
+
+ return endian.swapIfBe(u160, r);
+ }
+
+ fn round(d: &Self, b: []const u8) {
+ debug.assert(b.len == 64);
+
+ var s: [16]u32 = undefined;
+
+ var v: [5]u32 = []u32 {
+ d.s[0], d.s[1], d.s[2], d.s[3], d.s[4],
+ };
+
+ const round0a = comptime []RoundParam {
+ Rp(0, 1, 2, 3, 4, 0),
+ Rp(4, 0, 1, 2, 3, 1),
+ Rp(3, 4, 0, 1, 2, 2),
+ Rp(2, 3, 4, 0, 1, 3),
+ Rp(1, 2, 3, 4, 0, 4),
+ Rp(0, 1, 2, 3, 4, 5),
+ Rp(4, 0, 1, 2, 3, 6),
+ Rp(3, 4, 0, 1, 2, 7),
+ Rp(2, 3, 4, 0, 1, 8),
+ Rp(1, 2, 3, 4, 0, 9),
+ Rp(0, 1, 2, 3, 4, 10),
+ Rp(4, 0, 1, 2, 3, 11),
+ Rp(3, 4, 0, 1, 2, 12),
+ Rp(2, 3, 4, 0, 1, 13),
+ Rp(1, 2, 3, 4, 0, 14),
+ Rp(0, 1, 2, 3, 4, 15),
+ };
+ inline for (round0a) |r| {
+ s[r.i] = (u32(b[r.i * 4 + 0]) << 24) |
+ (u32(b[r.i * 4 + 1]) << 16) |
+ (u32(b[r.i * 4 + 2]) << 8) |
+ (u32(b[r.i * 4 + 3]) << 0);
+
+ v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf]
+ +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d]));
+ v[r.b] = math.rotl(u32, v[r.b], u32(30));
+ }
+
+ const round0b = comptime []RoundParam {
+ Rp(4, 0, 1, 2, 3, 16),
+ Rp(3, 4, 0, 1, 2, 17),
+ Rp(2, 3, 4, 0, 1, 18),
+ Rp(1, 2, 3, 4, 0, 19),
+ };
+ inline for (round0b) |r| {
+ const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf];
+ s[r.i & 0xf] = math.rotl(u32, t, u32(1));
+
+ v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf]
+ +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d]));
+ v[r.b] = math.rotl(u32, v[r.b], u32(30));
+ }
+
+ const round1 = comptime []RoundParam {
+ Rp(0, 1, 2, 3, 4, 20),
+ Rp(4, 0, 1, 2, 3, 21),
+ Rp(3, 4, 0, 1, 2, 22),
+ Rp(2, 3, 4, 0, 1, 23),
+ Rp(1, 2, 3, 4, 0, 24),
+ Rp(0, 1, 2, 3, 4, 25),
+ Rp(4, 0, 1, 2, 3, 26),
+ Rp(3, 4, 0, 1, 2, 27),
+ Rp(2, 3, 4, 0, 1, 28),
+ Rp(1, 2, 3, 4, 0, 29),
+ Rp(0, 1, 2, 3, 4, 30),
+ Rp(4, 0, 1, 2, 3, 31),
+ Rp(3, 4, 0, 1, 2, 32),
+ Rp(2, 3, 4, 0, 1, 33),
+ Rp(1, 2, 3, 4, 0, 34),
+ Rp(0, 1, 2, 3, 4, 35),
+ Rp(4, 0, 1, 2, 3, 36),
+ Rp(3, 4, 0, 1, 2, 37),
+ Rp(2, 3, 4, 0, 1, 38),
+ Rp(1, 2, 3, 4, 0, 39),
+ };
+ inline for (round1) |r| {
+ const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf];
+ s[r.i & 0xf] = math.rotl(u32, t, u32(1));
+
+ v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x6ED9EBA1 +% s[r.i & 0xf]
+ +% (v[r.b] ^ v[r.c] ^ v[r.d]);
+ v[r.b] = math.rotl(u32, v[r.b], u32(30));
+ }
+
+ const round2 = comptime []RoundParam {
+ Rp(0, 1, 2, 3, 4, 40),
+ Rp(4, 0, 1, 2, 3, 41),
+ Rp(3, 4, 0, 1, 2, 42),
+ Rp(2, 3, 4, 0, 1, 43),
+ Rp(1, 2, 3, 4, 0, 44),
+ Rp(0, 1, 2, 3, 4, 45),
+ Rp(4, 0, 1, 2, 3, 46),
+ Rp(3, 4, 0, 1, 2, 47),
+ Rp(2, 3, 4, 0, 1, 48),
+ Rp(1, 2, 3, 4, 0, 49),
+ Rp(0, 1, 2, 3, 4, 50),
+ Rp(4, 0, 1, 2, 3, 51),
+ Rp(3, 4, 0, 1, 2, 52),
+ Rp(2, 3, 4, 0, 1, 53),
+ Rp(1, 2, 3, 4, 0, 54),
+ Rp(0, 1, 2, 3, 4, 55),
+ Rp(4, 0, 1, 2, 3, 56),
+ Rp(3, 4, 0, 1, 2, 57),
+ Rp(2, 3, 4, 0, 1, 58),
+ Rp(1, 2, 3, 4, 0, 59),
+ };
+ inline for (round2) |r| {
+ const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf];
+ s[r.i & 0xf] = math.rotl(u32, t, u32(1));
+
+ v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x8F1BBCDC +% s[r.i & 0xf]
+ +% ((v[r.b] & v[r.c]) ^ (v[r.b] & v[r.d]) ^ (v[r.c] & v[r.d]));
+ v[r.b] = math.rotl(u32, v[r.b], u32(30));
+ }
+
+ const round3 = comptime []RoundParam {
+ Rp(0, 1, 2, 3, 4, 60),
+ Rp(4, 0, 1, 2, 3, 61),
+ Rp(3, 4, 0, 1, 2, 62),
+ Rp(2, 3, 4, 0, 1, 63),
+ Rp(1, 2, 3, 4, 0, 64),
+ Rp(0, 1, 2, 3, 4, 65),
+ Rp(4, 0, 1, 2, 3, 66),
+ Rp(3, 4, 0, 1, 2, 67),
+ Rp(2, 3, 4, 0, 1, 68),
+ Rp(1, 2, 3, 4, 0, 69),
+ Rp(0, 1, 2, 3, 4, 70),
+ Rp(4, 0, 1, 2, 3, 71),
+ Rp(3, 4, 0, 1, 2, 72),
+ Rp(2, 3, 4, 0, 1, 73),
+ Rp(1, 2, 3, 4, 0, 74),
+ Rp(0, 1, 2, 3, 4, 75),
+ Rp(4, 0, 1, 2, 3, 76),
+ Rp(3, 4, 0, 1, 2, 77),
+ Rp(2, 3, 4, 0, 1, 78),
+ Rp(1, 2, 3, 4, 0, 79),
+ };
+ inline for (round3) |r| {
+ const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf];
+ s[r.i & 0xf] = math.rotl(u32, t, u32(1));
+
+ v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0xCA62C1D6 +% s[r.i & 0xf]
+ +% (v[r.b] ^ v[r.c] ^ v[r.d]);
+ v[r.b] = math.rotl(u32, v[r.b], u32(30));
+ }
+
+ d.s[0] +%= v[0];
+ d.s[1] +%= v[1];
+ d.s[2] +%= v[2];
+ d.s[3] +%= v[3];
+ d.s[4] +%= v[4];
+ }
+};
+
+test "sha1 single" {
+ debug.assert(0xda39a3ee5e6b4b0d3255bfef95601890afd80709 == Sha1.hash(""));
+ debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == Sha1.hash("abc"));
+ debug.assert(0xa49b2446a02c645bf419f995b67091253a04a259 == Sha1.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
+}
+
+test "sha1 streaming" {
+ var h = Sha1.init();
+
+ debug.assert(0xda39a3ee5e6b4b0d3255bfef95601890afd80709 == h.final());
+
+ h.reset();
+ h.update("abc");
+ debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == h.final());
+
+ h.reset();
+ h.update("a");
+ h.update("b");
+ h.update("c");
+ debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == h.final());
+}
diff --git a/std/index.zig b/std/index.zig
index a9a0038e60b6a68ac3b0ddd375ad87690f4c3cab..3986063068a23bf57e6256048643126f688f2796 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -10,6 +10,7 @@ pub const LinkedList = @import("linked_list.zig").LinkedList;
pub const base64 = @import("base64.zig");
pub const build = @import("build.zig");
pub const c = @import("c/index.zig");
+pub const crypto = @import("crypto/index.zig");
pub const cstr = @import("cstr.zig");
pub const debug = @import("debug/index.zig");
pub const dwarf = @import("dwarf.zig");
@@ -39,6 +40,7 @@ test "std" {
_ = @import("base64.zig");
_ = @import("build.zig");
_ = @import("c/index.zig");
+ _ = @import("crypto/index.zig");
_ = @import("cstr.zig");
_ = @import("debug/index.zig");
_ = @import("dwarf.zig");
--
2.54.0
From 4551489b924fad262eb3343b68fc4c0e18ed6a97 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 13 Jan 2018 01:00:50 -0500
Subject: [PATCH 05/25] typecheck the panic function
---
src/analyze.cpp | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 61d86b5eb2a9cd05971a4566aaf61eb0da01556d..b52cb019d39c3cede9f685cb891b27414d831ba1 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -2584,17 +2584,16 @@ static bool scope_is_root_decls(Scope *scope) {
static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntry *fn_type) {
add_node_error(g, proto_node,
- buf_sprintf("expected 'fn([]const u8) -> unreachable', found '%s'",
+ buf_sprintf("expected 'fn([]const u8, ?&builtin.StackTrace) -> unreachable', found '%s'",
buf_ptr(&fn_type->name)));
}
static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
- return; // TODO
AstNode *proto_node = panic_fn->proto_node;
assert(proto_node->type == NodeTypeFnProto);
TypeTableEntry *fn_type = panic_fn->type_entry;
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
- if (fn_type_id->param_count != 1) {
+ if (fn_type_id->param_count != 2) {
return wrong_panic_prototype(g, proto_node, fn_type);
}
TypeTableEntry *const_u8_ptr = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
@@ -2603,6 +2602,11 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
return wrong_panic_prototype(g, proto_node, fn_type);
}
+ TypeTableEntry *nullable_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g));
+ if (fn_type_id->param_info[1].type != nullable_ptr_to_stack_trace_type) {
+ return wrong_panic_prototype(g, proto_node, fn_type);
+ }
+
TypeTableEntry *actual_return_type = fn_type_id->return_type;
if (actual_return_type != g->builtin_types.entry_unreachable) {
return wrong_panic_prototype(g, proto_node, fn_type);
--
2.54.0
From 2659ac01be85662265be6e7fcdd8e03dcade1c50 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Sat, 13 Jan 2018 22:23:12 +1300
Subject: [PATCH 06/25] Add Sha2 functions
We take the fastest time measurement taken across multiple runs. Tested
across multiple compiler flags and the best chosen.
```
Cpu: Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz
Gcc: 7.2.1 20171224
Clang: 5.0.1
Zig: 0.1.1.304f6f1d
```
See https://www.nayuki.io/page/fast-sha2-hashes-in-x86-assembly.
```
Gcc -O2
219 Mb/s
Clang -O2
213 Mb/s
Zig --release-fast
284 Mb/s
Zig --release-safe
211 Mb/s
Zig
6 Mb/s
```
```
Gcc -O2
350 Mb/s
Clang -O2
354 Mb/s
Zig --release-fast
426 Mb/s
Zig --release-safe
300 Mb/s
Zig
11 Mb/s
```
---
CMakeLists.txt | 1 +
std/crypto/index.zig | 9 +-
std/crypto/sha2.zig | 660 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 669 insertions(+), 1 deletion(-)
create mode 100644 std/crypto/sha2.zig
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1e9691f1c620beef18f84d4fe58e42f278dc8f22..636c215f063e8f7e519211a3ea431bb9b2df51a0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -367,6 +367,7 @@ set(ZIG_STD_FILES
"crypto/index.zig"
"crypto/md5.zig"
"crypto/sha1.zig"
+ "crypto/sha2.zig"
"cstr.zig"
"debug/failing_allocator.zig"
"debug/index.zig"
diff --git a/std/crypto/index.zig b/std/crypto/index.zig
index eaa371555a626ffa188aa1eabac66165ae248683..27dd467bd0db9d283f7421e3e60f0876df69ee65 100644
--- a/std/crypto/index.zig
+++ b/std/crypto/index.zig
@@ -1,7 +1,14 @@
-pub const Sha1 = @import("md5.zig").Sha1;
pub const Md5 = @import("sha1.zig").Md5;
+pub const Sha1 = @import("md5.zig").Sha1;
+
+const sha2 = @import("sha2.zig");
+pub const Sha224 = sha2.Sha224;
+pub const Sha256 = sha2.Sha256;
+pub const Sha384 = sha2.Sha384;
+pub const Sha512 = sha2.Sha512;
test "crypto" {
_ = @import("md5.zig");
_ = @import("sha1.zig");
+ _ = @import("sha2.zig");
}
diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig
new file mode 100644
index 0000000000000000000000000000000000000000..4fe46b551c175cd66378b07aac0c1d062ce04128
--- /dev/null
+++ b/std/crypto/sha2.zig
@@ -0,0 +1,660 @@
+const mem = @import("../mem.zig");
+const math = @import("../math/index.zig");
+const endian = @import("../endian.zig");
+const debug = @import("../debug/index.zig");
+
+/////////////////////
+// Sha224 + Sha256
+
+const RoundParam256 = struct {
+ a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: u32,
+ i: u32, k: u32,
+};
+
+fn Rp256(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: u32, i: u32, k: u32) -> RoundParam256 {
+ return RoundParam256 { .a = a, .b = b, .c = c, .d = d, .e = e, .f = f, .g = g, .h = h, .i = i, .k = k };
+}
+
+const Sha2Params32 = struct {
+ iv0: u32,
+ iv1: u32,
+ iv2: u32,
+ iv3: u32,
+ iv4: u32,
+ iv5: u32,
+ iv6: u32,
+ iv7: u32,
+ out_len: u32,
+};
+
+const Sha224Params = Sha2Params32 {
+ .iv0 = 0xC1059ED8,
+ .iv1 = 0x367CD507,
+ .iv2 = 0x3070DD17,
+ .iv3 = 0xF70E5939,
+ .iv4 = 0xFFC00B31,
+ .iv5 = 0x68581511,
+ .iv6 = 0x64F98FA7,
+ .iv7 = 0xBEFA4FA4,
+ .out_len = 224,
+};
+
+const Sha256Params = Sha2Params32 {
+ .iv0 = 0x6A09E667,
+ .iv1 = 0xBB67AE85,
+ .iv2 = 0x3C6EF372,
+ .iv3 = 0xA54FF53A,
+ .iv4 = 0x510E527F,
+ .iv5 = 0x9B05688C,
+ .iv6 = 0x1F83D9AB,
+ .iv7 = 0x5BE0CD19,
+ .out_len = 256,
+};
+
+pub const Sha224 = Sha2_32(Sha224Params);
+pub const Sha256 = Sha2_32(Sha256Params);
+
+fn Sha2_32(comptime params: Sha2Params32) -> type { return struct {
+ const Self = this;
+ const ReturnType = @IntType(false, params.out_len);
+
+ s: [8]u32,
+ // Streaming Cache
+ buf: [64]u8,
+ buf_len: u8,
+ total_len: u64,
+
+ pub fn init() -> Self {
+ var d: Self = undefined;
+ d.reset();
+ return d;
+ }
+
+ pub fn reset(d: &Self) {
+ d.s[0] = params.iv0;
+ d.s[1] = params.iv1;
+ d.s[2] = params.iv2;
+ d.s[3] = params.iv3;
+ d.s[4] = params.iv4;
+ d.s[5] = params.iv5;
+ d.s[6] = params.iv6;
+ d.s[7] = params.iv7;
+ d.buf_len = 0;
+ d.total_len = 0;
+ }
+
+ pub fn hash(b: []const u8) -> ReturnType {
+ var d = Self.init();
+ d.update(b);
+ return d.final();
+ }
+
+ pub fn update(d: &Self, b: []const u8) {
+ var off: usize = 0;
+
+ // Partial buffer exists from previous update. Copy into buffer then hash.
+ if (d.buf_len != 0 and d.buf_len + b.len > 64) {
+ off += 64 - d.buf_len;
+ mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
+
+ d.round(d.buf[0..]);
+ d.buf_len = 0;
+ }
+
+ // Full middle blocks.
+ while (off + 64 < b.len) : (off += 64) {
+ d.round(b[off..off + 64]);
+ }
+
+ // Copy any remainder for next pass.
+ mem.copy(u8, d.buf[d.buf_len..], b[off..]);
+ d.buf_len += u8(b[off..].len);
+
+ d.total_len += b.len;
+ }
+
+ pub fn final(d: &Self) -> ReturnType {
+ // The buffer here will never be completely full.
+ mem.set(u8, d.buf[d.buf_len..], 0);
+
+ // Append padding bits.
+ d.buf[d.buf_len] = 0x80;
+ d.buf_len += 1;
+
+ // > 448 mod 512 so need to add an extra round to wrap around.
+ if (64 - d.buf_len < 8) {
+ d.round(d.buf[0..]);
+ mem.set(u8, d.buf[0..], 0);
+ }
+
+ // Append message length.
+ var i: usize = 1;
+ var len = d.total_len >> 5;
+ d.buf[63] = u8(d.total_len & 0x1f) << 3;
+ while (i < 8) : (i += 1) {
+ d.buf[63 - i] = u8(len & 0xff);
+ len >>= 8;
+ }
+
+ d.round(d.buf[0..]);
+
+ // May truncate for possible 224 output
+ const rr = d.s[0 .. params.out_len / 32];
+
+ var j: u8 = u8(rr.len - 1) * 32;
+ var r: ReturnType = 0;
+ for (rr) |p| {
+ r |= ReturnType(p) << j;
+ j -%= 32;
+ }
+
+ return endian.swapIfBe(ReturnType, r);
+ }
+
+ fn round(d: &Self, b: []const u8) {
+ debug.assert(b.len == 64);
+
+ var s: [64]u32 = undefined;
+
+ // ERROR: Cannot unroll at compile-time.
+ var i: usize = 0;
+ while (i < 16) : (i += 1) {
+ s[i] = 0;
+ s[i] |= u32(b[i*4+0]) << 24;
+ s[i] |= u32(b[i*4+1]) << 16;
+ s[i] |= u32(b[i*4+2]) << 8;
+ s[i] |= u32(b[i*4+3]) << 0;
+ }
+ while (i < 64) : (i += 1) {
+ s[i] =
+ s[i-16] +% s[i-7] +%
+ (math.rotr(u32, s[i-15], u32(7)) ^ math.rotr(u32, s[i-15], u32(18)) ^ (s[i-15] >> 3)) +%
+ (math.rotr(u32, s[i-2], u32(17)) ^ math.rotr(u32, s[i-2], u32(19)) ^ (s[i-2] >> 10));
+ }
+
+ var v: [8]u32 = []u32 {
+ d.s[0], d.s[1], d.s[2], d.s[3], d.s[4], d.s[5], d.s[6], d.s[7],
+ };
+
+ const round0 = comptime []RoundParam256 {
+ Rp256(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98),
+ Rp256(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x71374491),
+ Rp256(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCF),
+ Rp256(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA5),
+ Rp256(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25B),
+ Rp256(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1),
+ Rp256(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4),
+ Rp256(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5),
+ Rp256(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98),
+ Rp256(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B01),
+ Rp256(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE),
+ Rp256(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3),
+ Rp256(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74),
+ Rp256(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE),
+ Rp256(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A7),
+ Rp256(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174),
+ Rp256(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C1),
+ Rp256(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786),
+ Rp256(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC6),
+ Rp256(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC),
+ Rp256(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F),
+ Rp256(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA),
+ Rp256(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DC),
+ Rp256(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA),
+ Rp256(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152),
+ Rp256(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D),
+ Rp256(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C8),
+ Rp256(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7),
+ Rp256(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF3),
+ Rp256(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147),
+ Rp256(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351),
+ Rp256(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x14292967),
+ Rp256(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A85),
+ Rp256(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B2138),
+ Rp256(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC),
+ Rp256(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D13),
+ Rp256(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A7354),
+ Rp256(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB),
+ Rp256(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E),
+ Rp256(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C85),
+ Rp256(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A1),
+ Rp256(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664B),
+ Rp256(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70),
+ Rp256(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A3),
+ Rp256(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819),
+ Rp256(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD6990624),
+ Rp256(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E3585),
+ Rp256(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA070),
+ Rp256(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116),
+ Rp256(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C08),
+ Rp256(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774C),
+ Rp256(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5),
+ Rp256(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3),
+ Rp256(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4A),
+ Rp256(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F),
+ Rp256(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3),
+ Rp256(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE),
+ Rp256(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F),
+ Rp256(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814),
+ Rp256(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC70208),
+ Rp256(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA),
+ Rp256(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEB),
+ Rp256(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7),
+ Rp256(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2),
+ };
+ inline for (round0) |r| {
+ v[r.h] =
+ v[r.h] +%
+ (math.rotr(u32, v[r.e], u32(6)) ^ math.rotr(u32, v[r.e], u32(11)) ^ math.rotr(u32, v[r.e], u32(25))) +%
+ (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +%
+ r.k +% s[r.i];
+
+ v[r.d] = v[r.d] +% v[r.h];
+
+ v[r.h] =
+ v[r.h] +%
+ (math.rotr(u32, v[r.a], u32(2)) ^ math.rotr(u32, v[r.a], u32(13)) ^ math.rotr(u32, v[r.a], u32(22))) +%
+ ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c]));
+ }
+
+ d.s[0] +%= v[0];
+ d.s[1] +%= v[1];
+ d.s[2] +%= v[2];
+ d.s[3] +%= v[3];
+ d.s[4] +%= v[4];
+ d.s[5] +%= v[5];
+ d.s[6] +%= v[6];
+ d.s[7] +%= v[7];
+ }
+};}
+
+test "sha224 single" {
+ debug.assert(0xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f == Sha224.hash(""));
+ debug.assert(0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 == Sha224.hash("abc"));
+ debug.assert(0xc97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3 == Sha224.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
+}
+
+test "sha224 streaming" {
+ var h = Sha224.init();
+
+ debug.assert(0xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f == h.final());
+
+ h.reset();
+ h.update("abc");
+ debug.assert(0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 == h.final());
+
+ h.reset();
+ h.update("a");
+ h.update("b");
+ h.update("c");
+ debug.assert(0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 == h.final());
+}
+
+test "sha256 single" {
+ debug.assert(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == Sha256.hash(""));
+ debug.assert(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad == Sha256.hash("abc"));
+ debug.assert(0xcf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1 == Sha256.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
+}
+
+test "sha256 streaming" {
+ var h = Sha256.init();
+
+ debug.assert(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == h.final());
+
+ h.reset();
+ h.update("abc");
+ debug.assert(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad == h.final());
+
+ h.reset();
+ h.update("a");
+ h.update("b");
+ h.update("c");
+ debug.assert(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad == h.final());
+}
+
+
+/////////////////////
+// Sha384 + Sha512
+
+const RoundParam512 = struct {
+ a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize,
+ i: usize, k: u64,
+};
+
+fn Rp512(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u64) -> RoundParam512 {
+ return RoundParam512 { .a = a, .b = b, .c = c, .d = d, .e = e, .f = f, .g = g, .h = h, .i = i, .k = k };
+}
+
+const Sha2Params64 = struct {
+ iv0: u64,
+ iv1: u64,
+ iv2: u64,
+ iv3: u64,
+ iv4: u64,
+ iv5: u64,
+ iv6: u64,
+ iv7: u64,
+ out_len: usize,
+};
+
+const Sha384Params = Sha2Params64 {
+ .iv0 = 0xCBBB9D5DC1059ED8,
+ .iv1 = 0x629A292A367CD507,
+ .iv2 = 0x9159015A3070DD17,
+ .iv3 = 0x152FECD8F70E5939,
+ .iv4 = 0x67332667FFC00B31,
+ .iv5 = 0x8EB44A8768581511,
+ .iv6 = 0xDB0C2E0D64F98FA7,
+ .iv7 = 0x47B5481DBEFA4FA4,
+ .out_len = 384,
+};
+
+const Sha512Params = Sha2Params64 {
+ .iv0 = 0x6A09E667F3BCC908,
+ .iv1 = 0xBB67AE8584CAA73B,
+ .iv2 = 0x3C6EF372FE94F82B,
+ .iv3 = 0xA54FF53A5F1D36F1,
+ .iv4 = 0x510E527FADE682D1,
+ .iv5 = 0x9B05688C2B3E6C1F,
+ .iv6 = 0x1F83D9ABFB41BD6B,
+ .iv7 = 0x5BE0CD19137E2179,
+ .out_len = 512
+};
+
+pub const Sha384 = Sha2_64(Sha384Params);
+pub const Sha512 = Sha2_64(Sha512Params);
+
+fn Sha2_64(comptime params: Sha2Params64) -> type { return struct {
+ const Self = this;
+ const ReturnType = @IntType(false, params.out_len);
+ const u9 = @IntType(false, 9);
+
+ s: [8]u64,
+ // Streaming Cache
+ buf: [128]u8,
+ buf_len: u8,
+ total_len: u64,
+
+ pub fn init() -> Self {
+ var d: Self = undefined;
+ d.reset();
+ return d;
+ }
+
+ pub fn reset(d: &Self) {
+ d.s[0] = params.iv0;
+ d.s[1] = params.iv1;
+ d.s[2] = params.iv2;
+ d.s[3] = params.iv3;
+ d.s[4] = params.iv4;
+ d.s[5] = params.iv5;
+ d.s[6] = params.iv6;
+ d.s[7] = params.iv7;
+ d.buf_len = 0;
+ d.total_len = 0;
+ }
+
+ pub fn hash(b: []const u8) -> ReturnType {
+ var d = Self.init();
+ d.update(b);
+ return d.final();
+ }
+
+ pub fn update(d: &Self, b: []const u8) {
+ var off: usize = 0;
+
+ // Partial buffer exists from previous update. Copy into buffer then hash.
+ if (d.buf_len != 0 and d.buf_len + b.len > 128) {
+ off += 128 - d.buf_len;
+ mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
+
+ d.round(d.buf[0..]);
+ d.buf_len = 0;
+ }
+
+ // Full middle blocks.
+ while (off + 128 < b.len) : (off += 128) {
+ d.round(b[off..off + 128]);
+ }
+
+ // Copy any remainder for next pass.
+ mem.copy(u8, d.buf[d.buf_len..], b[off..]);
+ d.buf_len += u8(b[off..].len);
+
+ d.total_len += b.len;
+ }
+
+ pub fn final(d: &Self) -> ReturnType {
+ // The buffer here will never be completely full.
+ mem.set(u8, d.buf[d.buf_len..], 0);
+
+ // Append padding bits.
+ d.buf[d.buf_len] = 0x80;
+ d.buf_len += 1;
+
+ // > 896 mod 1024 so need to add an extra round to wrap around.
+ if (128 - d.buf_len < 16) {
+ d.round(d.buf[0..]);
+ mem.set(u8, d.buf[0..], 0);
+ }
+
+ // Append message length.
+ var i: usize = 1;
+ var len = d.total_len >> 5;
+ d.buf[127] = u8(d.total_len & 0x1f) << 3;
+ while (i < 16) : (i += 1) {
+ d.buf[127 - i] = u8(len & 0xff);
+ len >>= 8;
+ }
+
+ d.round(d.buf[0..]);
+
+ // May truncate for possible 384 output
+ const rr = d.s[0 .. params.out_len / 64];
+
+ var j: u9 = u9(rr.len - 1) * 64;
+ var r: ReturnType = 0;
+ for (rr) |p| {
+ r |= ReturnType(p) << j;
+ j -%= 64;
+ }
+
+ return endian.swapIfBe(ReturnType, r);
+ }
+
+ fn round(d: &Self, b: []const u8) {
+ debug.assert(b.len == 128);
+
+ var s: [80]u64 = undefined;
+
+ // ERROR: Cannot unroll at compile-time.
+ var i: usize = 0;
+ while (i < 16) : (i += 1) {
+ s[i] = 0;
+ s[i] |= u64(b[i*8+0]) << 56;
+ s[i] |= u64(b[i*8+1]) << 48;
+ s[i] |= u64(b[i*8+2]) << 40;
+ s[i] |= u64(b[i*8+3]) << 32;
+ s[i] |= u64(b[i*8+4]) << 24;
+ s[i] |= u64(b[i*8+5]) << 16;
+ s[i] |= u64(b[i*8+6]) << 8;
+ s[i] |= u64(b[i*8+7]) << 0;
+ }
+ while (i < 80) : (i += 1) {
+ s[i] =
+ s[i-16] +% s[i-7] +%
+ (math.rotr(u64, s[i-15], u64(1)) ^ math.rotr(u64, s[i-15], u64(8)) ^ (s[i-15] >> 7)) +%
+ (math.rotr(u64, s[i-2], u64(19)) ^ math.rotr(u64, s[i-2], u64(61)) ^ (s[i-2] >> 6));
+ }
+
+ var v: [8]u64 = []u64 {
+ d.s[0], d.s[1], d.s[2], d.s[3], d.s[4], d.s[5], d.s[6], d.s[7],
+ };
+
+ const round0 = comptime []RoundParam512 {
+ Rp512(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98D728AE22),
+ Rp512(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x7137449123EF65CD),
+ Rp512(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCFEC4D3B2F),
+ Rp512(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA58189DBBC),
+ Rp512(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25BF348B538),
+ Rp512(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1B605D019),
+ Rp512(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4AF194F9B),
+ Rp512(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5DA6D8118),
+ Rp512(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98A3030242),
+ Rp512(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B0145706FBE),
+ Rp512(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE4EE4B28C),
+ Rp512(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3D5FFB4E2),
+ Rp512(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74F27B896F),
+ Rp512(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE3B1696B1),
+ Rp512(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A725C71235),
+ Rp512(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174CF692694),
+ Rp512(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C19EF14AD2),
+ Rp512(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786384F25E3),
+ Rp512(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC68B8CD5B5),
+ Rp512(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC77AC9C65),
+ Rp512(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F592B0275),
+ Rp512(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA6EA6E483),
+ Rp512(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DCBD41FBD4),
+ Rp512(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA831153B5),
+ Rp512(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152EE66DFAB),
+ Rp512(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D2DB43210),
+ Rp512(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C898FB213F),
+ Rp512(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7BEEF0EE4),
+ Rp512(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF33DA88FC2),
+ Rp512(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147930AA725),
+ Rp512(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351E003826F),
+ Rp512(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x142929670A0E6E70),
+ Rp512(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A8546D22FFC),
+ Rp512(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B21385C26C926),
+ Rp512(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC5AC42AED),
+ Rp512(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D139D95B3DF),
+ Rp512(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A73548BAF63DE),
+ Rp512(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB3C77B2A8),
+ Rp512(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E47EDAEE6),
+ Rp512(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C851482353B),
+ Rp512(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A14CF10364),
+ Rp512(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664BBC423001),
+ Rp512(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70D0F89791),
+ Rp512(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A30654BE30),
+ Rp512(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819D6EF5218),
+ Rp512(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD69906245565A910),
+ Rp512(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E35855771202A),
+ Rp512(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA07032BBD1B8),
+ Rp512(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116B8D2D0C8),
+ Rp512(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C085141AB53),
+ Rp512(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774CDF8EEB99),
+ Rp512(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5E19B48A8),
+ Rp512(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3C5C95A63),
+ Rp512(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4AE3418ACB),
+ Rp512(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F7763E373),
+ Rp512(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3D6B2B8A3),
+ Rp512(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE5DEFB2FC),
+ Rp512(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F43172F60),
+ Rp512(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814A1F0AB72),
+ Rp512(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC702081A6439EC),
+ Rp512(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA23631E28),
+ Rp512(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEBDE82BDE9),
+ Rp512(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7B2C67915),
+ Rp512(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2E372532B),
+ Rp512(0, 1, 2, 3, 4, 5, 6, 7, 64, 0xCA273ECEEA26619C),
+ Rp512(7, 0, 1, 2, 3, 4, 5, 6, 65, 0xD186B8C721C0C207),
+ Rp512(6, 7, 0, 1, 2, 3, 4, 5, 66, 0xEADA7DD6CDE0EB1E),
+ Rp512(5, 6, 7, 0, 1, 2, 3, 4, 67, 0xF57D4F7FEE6ED178),
+ Rp512(4, 5, 6, 7, 0, 1, 2, 3, 68, 0x06F067AA72176FBA),
+ Rp512(3, 4, 5, 6, 7, 0, 1, 2, 69, 0x0A637DC5A2C898A6),
+ Rp512(2, 3, 4, 5, 6, 7, 0, 1, 70, 0x113F9804BEF90DAE),
+ Rp512(1, 2, 3, 4, 5, 6, 7, 0, 71, 0x1B710B35131C471B),
+ Rp512(0, 1, 2, 3, 4, 5, 6, 7, 72, 0x28DB77F523047D84),
+ Rp512(7, 0, 1, 2, 3, 4, 5, 6, 73, 0x32CAAB7B40C72493),
+ Rp512(6, 7, 0, 1, 2, 3, 4, 5, 74, 0x3C9EBE0A15C9BEBC),
+ Rp512(5, 6, 7, 0, 1, 2, 3, 4, 75, 0x431D67C49C100D4C),
+ Rp512(4, 5, 6, 7, 0, 1, 2, 3, 76, 0x4CC5D4BECB3E42B6),
+ Rp512(3, 4, 5, 6, 7, 0, 1, 2, 77, 0x597F299CFC657E2A),
+ Rp512(2, 3, 4, 5, 6, 7, 0, 1, 78, 0x5FCB6FAB3AD6FAEC),
+ Rp512(1, 2, 3, 4, 5, 6, 7, 0, 79, 0x6C44198C4A475817),
+ };
+ inline for (round0) |r| {
+ v[r.h] =
+ v[r.h] +%
+ (math.rotr(u64, v[r.e], u64(14)) ^ math.rotr(u64, v[r.e], u64(18)) ^ math.rotr(u64, v[r.e], u64(41))) +%
+ (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +%
+ r.k +% s[r.i];
+
+ v[r.d] = v[r.d] +% v[r.h];
+
+ v[r.h] =
+ v[r.h] +%
+ (math.rotr(u64, v[r.a], u64(28)) ^ math.rotr(u64, v[r.a], u64(34)) ^ math.rotr(u64, v[r.a], u64(39))) +%
+ ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c]));
+ }
+
+ d.s[0] +%= v[0];
+ d.s[1] +%= v[1];
+ d.s[2] +%= v[2];
+ d.s[3] +%= v[3];
+ d.s[4] +%= v[4];
+ d.s[5] +%= v[5];
+ d.s[6] +%= v[6];
+ d.s[7] +%= v[7];
+ }
+};}
+
+test "sha384 single" {
+ const h1 = 0x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b;
+ debug.assert(h1 == Sha384.hash(""));
+
+ const h2 = 0xcb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7;
+ debug.assert(h2 == Sha384.hash("abc"));
+
+ const h3 = 0x09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039;
+ debug.assert(h3 == Sha384.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
+}
+
+test "sha384 streaming" {
+ var h = Sha384.init();
+
+ const h1 = 0x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b;
+ debug.assert(h1 == h.final());
+
+ const h2 = 0xcb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7;
+
+ h.reset();
+ h.update("abc");
+ debug.assert(h2 == h.final());
+
+ h.reset();
+ h.update("a");
+ h.update("b");
+ h.update("c");
+ debug.assert(h2 == h.final());
+}
+
+test "sha512 single" {
+ const h1 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e;
+ debug.assert(h1 == Sha512.hash(""));
+
+ const h2 = 0xddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f;
+ debug.assert(h2 == Sha512.hash("abc"));
+
+ const h3 = 0x8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909;
+ debug.assert(h3 == Sha512.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
+}
+
+test "sha512 streaming" {
+ var h = Sha512.init();
+
+ const h1 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e;
+ debug.assert(h1 == h.final());
+
+ const h2 = 0xddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f;
+
+ h.reset();
+ h.update("abc");
+ debug.assert(h2 == h.final());
+
+ h.reset();
+ h.update("a");
+ h.update("b");
+ h.update("c");
+ debug.assert(h2 == h.final());
+}
--
2.54.0
From 1f3ed5cf27e2d6d6fdb03e67b0327ae34b9fdf4c Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Sat, 13 Jan 2018 22:44:58 +1300
Subject: [PATCH 07/25] Change indexing variable types for crypto functions
---
std/crypto/md5.zig | 6 +++---
std/crypto/sha1.zig | 4 ++--
std/crypto/sha2.zig | 8 ++++----
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig
index a324e4b8c6ff2d4cf8a8c2b0d2f6485506fd6111..d1386d9be60cd75b1d320c45d8e8e657f997df34 100644
--- a/std/crypto/md5.zig
+++ b/std/crypto/md5.zig
@@ -4,11 +4,11 @@ const endian = @import("../endian.zig");
const debug = @import("../debug/index.zig");
const RoundParam = struct {
- a: u32, b: u32, c: u32, d: u32,
- k: u32, s: u32, t: u32
+ a: usize, b: usize, c: usize, d: usize,
+ k: usize, s: u32, t: u32
};
-fn Rp(a: u32, b: u32, c: u32, d: u32, k: u32, s: u5, t: u32) -> RoundParam {
+fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) -> RoundParam {
return RoundParam { .a = a, .b = b, .c = c, .d = d, .k = k, .s = s, .t = t };
}
diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig
index 6a6d8ba3eeb7528b630fe6aed5536d697dc565e3..5c8278700c78ac75345aac3f0cd8b7c91e803686 100644
--- a/std/crypto/sha1.zig
+++ b/std/crypto/sha1.zig
@@ -6,10 +6,10 @@ const debug = @import("../debug/index.zig");
pub const u160 = @IntType(false, 160);
const RoundParam = struct {
- a: u32, b: u32, c: u32, d: u32, e: u32, i: u32,
+ a: usize, b: usize, c: usize, d: usize, e: usize, i: u32,
};
-fn Rp(a: u32, b: u32, c: u32, d: u32, e: u32, i: u32) -> RoundParam {
+fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) -> RoundParam {
return RoundParam { .a = a, .b = b, .c = c, .d = d, .e = e, .i = i };
}
diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig
index 4fe46b551c175cd66378b07aac0c1d062ce04128..bc824bb3f344325e213b608e8fc09415a96e912b 100644
--- a/std/crypto/sha2.zig
+++ b/std/crypto/sha2.zig
@@ -7,11 +7,11 @@ const debug = @import("../debug/index.zig");
// Sha224 + Sha256
const RoundParam256 = struct {
- a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: u32,
- i: u32, k: u32,
+ a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize,
+ i: usize, k: u32,
};
-fn Rp256(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: u32, i: u32, k: u32) -> RoundParam256 {
+fn Rp256(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u32) -> RoundParam256 {
return RoundParam256 { .a = a, .b = b, .c = c, .d = d, .e = e, .f = f, .g = g, .h = h, .i = i, .k = k };
}
@@ -24,7 +24,7 @@ const Sha2Params32 = struct {
iv5: u32,
iv6: u32,
iv7: u32,
- out_len: u32,
+ out_len: usize,
};
const Sha224Params = Sha2Params32 {
--
2.54.0
From 9be9f1ad20ca6c7b2e1c09423c753b1a9e13049d Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Sun, 14 Jan 2018 09:58:30 +1300
Subject: [PATCH 08/25] Disable win32 tests for Sha2 + correct lengths
---
std/crypto/md5.zig | 3 ++-
std/crypto/sha2.zig | 43 ++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig
index d1386d9be60cd75b1d320c45d8e8e657f997df34..c01f288c56eb82bbf0b787085bbc3d51d69956cb 100644
--- a/std/crypto/md5.zig
+++ b/std/crypto/md5.zig
@@ -69,7 +69,8 @@ pub const Md5 = struct {
mem.copy(u8, d.buf[d.buf_len..], b[off..]);
d.buf_len += u8(b[off..].len);
- d.total_len += b.len;
+ // Md5 uses the bottom 64-bits for length padding
+ d.total_len +%= b.len;
}
pub fn final(d: &Self) -> u128 {
diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig
index bc824bb3f344325e213b608e8fc09415a96e912b..cfe4640fe788109ae78e22221283a65c39ef2938 100644
--- a/std/crypto/sha2.zig
+++ b/std/crypto/sha2.zig
@@ -2,6 +2,7 @@ const mem = @import("../mem.zig");
const math = @import("../math/index.zig");
const endian = @import("../endian.zig");
const debug = @import("../debug/index.zig");
+const builtin = @import("builtin");
/////////////////////
// Sha224 + Sha256
@@ -269,12 +270,22 @@ fn Sha2_32(comptime params: Sha2Params32) -> type { return struct {
};}
test "sha224 single" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
+
debug.assert(0xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f == Sha224.hash(""));
debug.assert(0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 == Sha224.hash("abc"));
debug.assert(0xc97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3 == Sha224.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
}
test "sha224 streaming" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
+
var h = Sha224.init();
debug.assert(0xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f == h.final());
@@ -291,12 +302,22 @@ test "sha224 streaming" {
}
test "sha256 single" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
+
debug.assert(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == Sha256.hash(""));
debug.assert(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad == Sha256.hash("abc"));
debug.assert(0xcf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1 == Sha256.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
}
test "sha256 streaming" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
+
var h = Sha256.init();
debug.assert(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == h.final());
@@ -373,7 +394,7 @@ fn Sha2_64(comptime params: Sha2Params64) -> type { return struct {
// Streaming Cache
buf: [128]u8,
buf_len: u8,
- total_len: u64,
+ total_len: u128,
pub fn init() -> Self {
var d: Self = undefined;
@@ -600,6 +621,11 @@ fn Sha2_64(comptime params: Sha2Params64) -> type { return struct {
};}
test "sha384 single" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
+
const h1 = 0x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b;
debug.assert(h1 == Sha384.hash(""));
@@ -611,6 +637,11 @@ test "sha384 single" {
}
test "sha384 streaming" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
+
var h = Sha384.init();
const h1 = 0x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b;
@@ -630,6 +661,11 @@ test "sha384 streaming" {
}
test "sha512 single" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
+
const h1 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e;
debug.assert(h1 == Sha512.hash(""));
@@ -641,6 +677,11 @@ test "sha512 single" {
}
test "sha512 streaming" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
+
var h = Sha512.init();
const h1 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e;
--
2.54.0
From 971a6fc5317b0e2d3a1f611702a4892f4aace942 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 14 Jan 2018 10:19:21 -0500
Subject: [PATCH 09/25] fix duplicate stack trace code
---
std/debug/index.zig | 258 +++++++++++++++---------------------
std/special/test_runner.zig | 12 +-
2 files changed, 115 insertions(+), 155 deletions(-)
diff --git a/std/debug/index.zig b/std/debug/index.zig
index fa2304079404c64d21e9fbcf6c7741d5bbf67549..f7f6ffa323e48ca835113acaf8640fea168d68f7 100644
--- a/std/debug/index.zig
+++ b/std/debug/index.zig
@@ -13,6 +13,10 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
error MissingDebugInfo;
error InvalidDebugInfo;
error UnsupportedDebugInfo;
+error UnknownObjectFormat;
+error TodoSupportCoffDebugInfo;
+error TodoSupportMachoDebugInfo;
+error TodoSupportCOFFDebugInfo;
/// Tries to write to stderr, unbuffered, and ignores any error returned.
@@ -40,13 +44,29 @@ fn getStderrStream() -> %&io.OutStream {
/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
pub fn dumpCurrentStackTrace() {
const stderr = getStderrStream() catch return;
- writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return;
+ const debug_info = openSelfDebugInfo(global_allocator) catch |err| {
+ stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
+ return;
+ };
+ defer debug_info.close();
+ writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), 1) catch |err| {
+ stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
+ return;
+ };
}
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) {
const stderr = getStderrStream() catch return;
- writeStackTrace(stack_trace, stderr, global_allocator, stderr_file.isTty()) catch return;
+ const debug_info = openSelfDebugInfo(global_allocator) catch |err| {
+ stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
+ return;
+ };
+ defer debug_info.close();
+ writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| {
+ stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
+ return;
+ };
}
/// This function invokes undefined behavior when `ok` is `false`.
@@ -94,7 +114,7 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
const stderr = getStderrStream() catch os.abort();
stderr.print(format ++ "\n", args) catch os.abort();
- writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort();
+ dumpCurrentStackTrace();
os.abort();
}
@@ -107,108 +127,88 @@ const RESET = "\x1b[0m";
error PathNotFound;
error InvalidDebugInfo;
-pub fn writeStackTrace(st_addrs: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool) -> %void {
- switch (builtin.object_format) {
- builtin.ObjectFormat.elf => {
- var stack_trace = ElfStackTrace {
- .self_exe_file = undefined,
- .elf = undefined,
- .debug_info = undefined,
- .debug_abbrev = undefined,
- .debug_str = undefined,
- .debug_line = undefined,
- .debug_ranges = null,
- .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
- .compile_unit_list = ArrayList(CompileUnit).init(allocator),
- };
- const st = &stack_trace;
- st.self_exe_file = try os.openSelfExe();
- defer st.self_exe_file.close();
+pub fn writeStackTrace(stack_trace: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
+ debug_info: &ElfStackTrace, tty_color: bool) -> %void
+{
+ var frame_index: usize = undefined;
+ var frames_left: usize = undefined;
+ if (stack_trace.index < stack_trace.instruction_addresses.len) {
+ frame_index = 0;
+ frames_left = stack_trace.index;
+ } else {
+ frame_index = (stack_trace.index + 1) % stack_trace.instruction_addresses.len;
+ frames_left = stack_trace.instruction_addresses.len;
+ }
- try st.elf.openFile(allocator, &st.self_exe_file);
- defer st.elf.close();
+ while (frames_left != 0) : ({
+ frames_left -= 1;
+ frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
+ }) {
+ const return_address = stack_trace.instruction_addresses[frame_index];
+ try printSourceAtAddress(debug_info, out_stream, return_address);
+ }
+}
- st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
- st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
- st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo;
- st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
- st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
- try scanAllCompileUnits(st);
+pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator,
+ debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) -> %void
+{
+ var ignored_count: usize = 0;
- var ignored_count: usize = 0;
+ var fp = @ptrToInt(@frameAddress());
+ while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) {
+ if (ignored_count < ignore_frame_count) {
+ ignored_count += 1;
+ continue;
+ }
- var frame_index: usize = undefined;
- var frames_left: usize = undefined;
- if (st_addrs.index < st_addrs.instruction_addresses.len) {
- frame_index = 0;
- frames_left = st_addrs.index;
+ const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize));
+ try printSourceAtAddress(debug_info, out_stream, return_address);
+ }
+}
+
+fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) -> %void {
+ // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
+ // at compile time. I'll call it issue #313
+ const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
+
+ const compile_unit = findCompileUnit(debug_info, address) catch {
+ try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
+ address);
+ return;
+ };
+ const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
+ if (getLineNumberInfo(debug_info, compile_unit, usize(address) - 1)) |line_info| {
+ defer line_info.deinit();
+ try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
+ DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
+ line_info.file_name, line_info.line, line_info.column,
+ address, compile_unit_name);
+ if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
+ if (line_info.column == 0) {
+ try out_stream.write("\n");
} else {
- frame_index = (st_addrs.index + 1) % st_addrs.instruction_addresses.len;
- frames_left = st_addrs.instruction_addresses.len;
+ {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
+ try out_stream.writeByte(' ');
+ }}
+ try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
}
-
- while (frames_left != 0) : ({frames_left -= 1; frame_index = (frame_index + 1) % st_addrs.instruction_addresses.len;}) {
- const return_address = st_addrs.instruction_addresses[frame_index];
-
- // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
- // at compile time. I'll call it issue #313
- const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
-
- const compile_unit = findCompileUnit(st, return_address) catch {
- try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
- return_address);
- continue;
- };
- const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name);
- if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| {
- defer line_info.deinit();
- try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
- DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
- line_info.file_name, line_info.line, line_info.column,
- return_address, compile_unit_name);
- if (printLineFromFile(st.allocator(), out_stream, line_info)) {
- if (line_info.column == 0) {
- try out_stream.write("\n");
- } else {
- {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
- try out_stream.writeByte(' ');
- }}
- try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
- }
- } else |err| switch (err) {
- error.EndOfFile, error.PathNotFound => {},
- else => return err,
- }
- } else |err| switch (err) {
- error.MissingDebugInfo, error.InvalidDebugInfo => {
- try out_stream.print(ptr_hex ++ " in ??? ({})\n",
- return_address, compile_unit_name);
- },
- else => return err,
- }
- }
- },
- builtin.ObjectFormat.coff => {
- try out_stream.write("(stack trace unavailable for COFF object format)\n");
- },
- builtin.ObjectFormat.macho => {
- try out_stream.write("(stack trace unavailable for Mach-O object format)\n");
- },
- builtin.ObjectFormat.wasm => {
- try out_stream.write("(stack trace unavailable for WASM object format)\n");
- },
- builtin.ObjectFormat.unknown => {
- try out_stream.write("(stack trace unavailable for unknown object format)\n");
- },
+ } else |err| switch (err) {
+ error.EndOfFile, error.PathNotFound => {},
+ else => return err,
+ }
+ } else |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => {
+ try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
+ },
+ else => return err,
}
}
-pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool,
- ignore_frame_count: usize) -> %void
-{
+pub fn openSelfDebugInfo(allocator: &mem.Allocator) -> %&ElfStackTrace {
switch (builtin.object_format) {
builtin.ObjectFormat.elf => {
- var stack_trace = ElfStackTrace {
+ const st = try allocator.create(ElfStackTrace);
+ *st = ElfStackTrace {
.self_exe_file = undefined,
.elf = undefined,
.debug_info = undefined,
@@ -219,12 +219,11 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
.compile_unit_list = ArrayList(CompileUnit).init(allocator),
};
- const st = &stack_trace;
st.self_exe_file = try os.openSelfExe();
- defer st.self_exe_file.close();
+ %defer st.self_exe_file.close();
try st.elf.openFile(allocator, &st.self_exe_file);
- defer st.elf.close();
+ %defer st.elf.close();
st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
@@ -232,67 +231,19 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
try scanAllCompileUnits(st);
-
- var ignored_count: usize = 0;
-
- var fp = @ptrToInt(@frameAddress());
- while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) {
- if (ignored_count < ignore_frame_count) {
- ignored_count += 1;
- continue;
- }
-
- const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize));
-
- // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
- // at compile time. I'll call it issue #313
- const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
-
- const compile_unit = findCompileUnit(st, return_address) catch {
- try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
- return_address);
- continue;
- };
- const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name);
- if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| {
- defer line_info.deinit();
- try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
- DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
- line_info.file_name, line_info.line, line_info.column,
- return_address, compile_unit_name);
- if (printLineFromFile(st.allocator(), out_stream, line_info)) {
- if (line_info.column == 0) {
- try out_stream.write("\n");
- } else {
- {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
- try out_stream.writeByte(' ');
- }}
- try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
- }
- } else |err| switch (err) {
- error.EndOfFile, error.PathNotFound => {},
- else => return err,
- }
- } else |err| switch (err) {
- error.MissingDebugInfo, error.InvalidDebugInfo => {
- try out_stream.print(ptr_hex ++ " in ??? ({})\n",
- return_address, compile_unit_name);
- },
- else => return err,
- }
- }
+ return st;
},
builtin.ObjectFormat.coff => {
- try out_stream.write("(stack trace unavailable for COFF object format)\n");
+ return error.TodoSupportCoffDebugInfo;
},
builtin.ObjectFormat.macho => {
- try out_stream.write("(stack trace unavailable for Mach-O object format)\n");
+ return error.TodoSupportMachoDebugInfo;
},
builtin.ObjectFormat.wasm => {
- try out_stream.write("(stack trace unavailable for WASM object format)\n");
+ return error.TodoSupportCOFFDebugInfo;
},
builtin.ObjectFormat.unknown => {
- try out_stream.write("(stack trace unavailable for unknown object format)\n");
+ return error.UnknownObjectFormat;
},
}
}
@@ -330,7 +281,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
}
}
-const ElfStackTrace = struct {
+pub const ElfStackTrace = struct {
self_exe_file: io.File,
elf: elf.Elf,
debug_info: &elf.SectionHeader,
@@ -350,6 +301,11 @@ const ElfStackTrace = struct {
const in_stream = &in_file_stream.stream;
return readStringRaw(self.allocator(), in_stream);
}
+
+ pub fn close(self: &ElfStackTrace) {
+ self.self_exe_file.close();
+ self.elf.close();
+ }
};
const PcRange = struct {
diff --git a/std/special/test_runner.zig b/std/special/test_runner.zig
index 1a36f50b62ba64d1495008bea45186bf5850f135..027251c52c9521465832d6717e4af542f75c1cdd 100644
--- a/std/special/test_runner.zig
+++ b/std/special/test_runner.zig
@@ -8,10 +8,14 @@ pub fn main() -> %void {
for (test_fn_list) |test_fn, i| {
warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
- test_fn.func() catch |err| {
- warn("{}\n", err);
- return err;
- };
+ if (builtin.is_test) {
+ test_fn.func() catch unreachable;
+ } else {
+ test_fn.func() catch |err| {
+ warn("{}\n", err);
+ return err;
+ };
+ }
warn("OK\n");
}
--
2.54.0
From fa024f80924d35e4aac4b4edcac88193def03175 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 14 Jan 2018 14:35:43 -0500
Subject: [PATCH 10/25] error return trace pointer prefixes other params
instead of being last. This increases the chances that it can
remain in the same register between calls.
---
src/analyze.cpp | 18 +++++++++---------
src/codegen.cpp | 34 +++++++++++++++++++++++-----------
2 files changed, 32 insertions(+), 20 deletions(-)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index b52cb019d39c3cede9f685cb891b27414d831ba1..f7c0f832a2fbe063b3c474039f83497abeb753ae 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -925,7 +925,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
if (!skip_debug_info) {
bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) &&
handle_is_ptr(fn_type_id->return_type);
- bool last_arg_error_return_trace = fn_type_id->return_type->id == TypeTableEntryIdErrorUnion ||
+ bool prefix_arg_error_return_trace = fn_type_id->return_type->id == TypeTableEntryIdErrorUnion ||
fn_type_id->return_type->id == TypeTableEntryIdPureError;
// +1 for maybe making the first argument the return value
// +1 for maybe last argument the error return trace
@@ -951,6 +951,14 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
}
fn_type->data.fn.gen_return_type = gen_return_type;
+ if (prefix_arg_error_return_trace) {
+ TypeTableEntry *gen_type = get_ptr_to_stack_trace_type(g);
+ gen_param_types[gen_param_index] = gen_type->type_ref;
+ gen_param_index += 1;
+ // after the gen_param_index += 1 because 0 is the return type
+ param_di_types[gen_param_index] = gen_type->di_type;
+ }
+
fn_type->data.fn.gen_param_info = allocate(fn_type_id->param_count);
for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i];
@@ -980,14 +988,6 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
}
}
- if (last_arg_error_return_trace) {
- TypeTableEntry *gen_type = get_ptr_to_stack_trace_type(g);
- gen_param_types[gen_param_index] = gen_type->type_ref;
- gen_param_index += 1;
- // after the gen_param_index += 1 because 0 is the return type
- param_di_types[gen_param_index] = gen_type->di_type;
- }
-
fn_type->data.fn.gen_param_count = gen_param_index;
fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref,
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 97cab9a69f53b8dfd457c2339e71001c95de228f..8b2cf2bcd4158f3aad2498fc65dc664cadb0bb84 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -406,6 +406,16 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) {
zig_unreachable();
}
+static uint32_t get_err_ret_trace_arg_index(FnTableEntry *fn_table_entry) {
+ TypeTableEntry *fn_type = fn_table_entry->type_entry;
+ TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
+ if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdPureError) {
+ return UINT32_MAX;
+ }
+ bool first_arg_ret = type_has_bits(return_type) && handle_is_ptr(return_type);
+ return first_arg_ret ? 1 : 0;
+}
+
static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
if (fn_table_entry->llvm_value)
return fn_table_entry->llvm_value;
@@ -563,9 +573,10 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "byval");
}
}
- if (return_type->id == TypeTableEntryIdErrorUnion || return_type->id == TypeTableEntryIdPureError) {
- unsigned gen_index = LLVMCountParamTypes(fn_llvm_type) - 1;
- addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull");
+
+ uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(fn_table_entry);
+ if (err_ret_trace_arg_index != UINT32_MAX) {
+ addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
}
return fn_table_entry->llvm_value;
@@ -2400,8 +2411,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
TypeTableEntry *src_return_type = fn_type_id->return_type;
bool ret_has_bits = type_has_bits(src_return_type);
bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
- bool last_arg_err_ret_stack = src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError;
- size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (last_arg_err_ret_stack ? 1 : 0);
+ bool prefix_arg_err_ret_stack = src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError;
+ size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0);
bool is_var_args = fn_type_id->is_var_args;
LLVMValueRef *gen_param_values = allocate(actual_param_count);
size_t gen_param_index = 0;
@@ -2409,6 +2420,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
gen_param_values[gen_param_index] = instruction->tmp_ptr;
gen_param_index += 1;
}
+ if (prefix_arg_err_ret_stack) {
+ gen_param_values[gen_param_index] = g->cur_err_ret_trace_val;
+ gen_param_index += 1;
+ }
for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) {
IrInstruction *param_instruction = instruction->args[call_i];
TypeTableEntry *param_type = param_instruction->value.type;
@@ -2419,10 +2434,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
gen_param_index += 1;
}
}
- if (last_arg_err_ret_stack) {
- gen_param_values[gen_param_index] = g->cur_err_ret_trace_val;
- gen_param_index += 1;
- }
ZigLLVM_FnInline fn_inline;
switch (instruction->fn_inline) {
@@ -4578,8 +4589,9 @@ static void do_code_gen(CodeGen *g) {
build_all_basic_blocks(g, fn_table_entry);
clear_debug_source_node(g);
- if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) {
- g->cur_err_ret_trace_val = LLVMGetParam(fn, LLVMCountParamTypes(fn_table_entry->type_entry->data.fn.raw_type_ref) - 1);
+ uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(fn_table_entry);
+ if (err_ret_trace_arg_index != UINT32_MAX) {
+ g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index);
} else if (fn_table_entry->calls_errorable_function) {
g->cur_err_ret_trace_val = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type));
size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
--
2.54.0
From 793f031c4ca7fdd230ef262895acf3e454be49dd Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 14 Jan 2018 15:17:07 -0500
Subject: [PATCH 11/25] remove 32-bit windows from supported targets list
we still want to support it, but there are too many bugs
to claim that we support it right now.
See #537
---
README.md | 2 +-
src-self-hosted/parser.zig | 6 ------
std/crypto/sha2.zig | 40 --------------------------------------
std/io_test.zig | 5 -----
std/math/acosh.zig | 5 -----
std/math/cos.zig | 5 -----
std/math/cosh.zig | 5 -----
std/math/index.zig | 15 --------------
std/math/ln.zig | 5 -----
std/math/log.zig | 5 -----
std/math/log10.zig | 5 -----
std/math/log2.zig | 5 -----
std/math/pow.zig | 6 ------
std/math/round.zig | 5 -----
std/math/sin.zig | 5 -----
std/math/sinh.zig | 5 -----
std/math/tan.zig | 5 -----
std/math/tanh.zig | 5 -----
std/rand.zig | 20 -------------------
std/sort.zig | 20 -------------------
test/tests.zig | 5 -----
21 files changed, 1 insertion(+), 178 deletions(-)
diff --git a/README.md b/README.md
index 084d62a24ec506a658c0c2209f1f1b9a590bab91..6a9fa4e580e97916e0340312cb3d9abbaceb2d82 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@ that counts as "freestanding" for the purposes of this table.
| | freestanding | linux | macosx | windows | other |
|-------------|--------------|---------|---------|---------|---------|
-|i386 | OK | planned | OK | OK | planned |
+|i386 | OK | planned | OK | planned | planned |
|x86_64 | OK | OK | OK | OK | planned |
|arm | OK | planned | planned | N/A | planned |
|aarch64 | OK | planned | planned | planned | planned |
diff --git a/src-self-hosted/parser.zig b/src-self-hosted/parser.zig
index b4bf9a1377e7b36217e8f287bca25600442125bc..88b6dd9bb0a25c57ac2d1fa668fb08d2772d86c5 100644
--- a/src-self-hosted/parser.zig
+++ b/src-self-hosted/parser.zig
@@ -1146,12 +1146,6 @@ fn testCanonical(source: []const u8) {
}
test "zig fmt" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
-
testCanonical(
\\extern fn puts(s: &const u8) -> c_int;
\\
diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig
index cfe4640fe788109ae78e22221283a65c39ef2938..8eba24015595452efe7c3c9fd938484d0636fca5 100644
--- a/std/crypto/sha2.zig
+++ b/std/crypto/sha2.zig
@@ -270,22 +270,12 @@ fn Sha2_32(comptime params: Sha2Params32) -> type { return struct {
};}
test "sha224 single" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
-
debug.assert(0xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f == Sha224.hash(""));
debug.assert(0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 == Sha224.hash("abc"));
debug.assert(0xc97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3 == Sha224.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
}
test "sha224 streaming" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
-
var h = Sha224.init();
debug.assert(0xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f == h.final());
@@ -302,22 +292,12 @@ test "sha224 streaming" {
}
test "sha256 single" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
-
debug.assert(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == Sha256.hash(""));
debug.assert(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad == Sha256.hash("abc"));
debug.assert(0xcf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1 == Sha256.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
}
test "sha256 streaming" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
-
var h = Sha256.init();
debug.assert(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == h.final());
@@ -621,11 +601,6 @@ fn Sha2_64(comptime params: Sha2Params64) -> type { return struct {
};}
test "sha384 single" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
-
const h1 = 0x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b;
debug.assert(h1 == Sha384.hash(""));
@@ -637,11 +612,6 @@ test "sha384 single" {
}
test "sha384 streaming" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
-
var h = Sha384.init();
const h1 = 0x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b;
@@ -661,11 +631,6 @@ test "sha384 streaming" {
}
test "sha512 single" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
-
const h1 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e;
debug.assert(h1 == Sha512.hash(""));
@@ -677,11 +642,6 @@ test "sha512 single" {
}
test "sha512 streaming" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
-
var h = Sha512.init();
const h1 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e;
diff --git a/std/io_test.zig b/std/io_test.zig
index 3b1f6466163d5426ac47ee5af4a89dd52a4b1742..1767a546ea490c7b892df7340f641b34448f96ce 100644
--- a/std/io_test.zig
+++ b/std/io_test.zig
@@ -8,11 +8,6 @@ const os = std.os;
const builtin = @import("builtin");
test "write a file, read it, then delete it" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
var data: [1024]u8 = undefined;
var rng = Rand.init(1234);
rng.fillBytes(data[0..]);
diff --git a/std/math/acosh.zig b/std/math/acosh.zig
index 150bcd4543af42997b8b7b6f2096e9dc2a822e81..91094302dd03d6b07ac590d05855065c7aa05454 100644
--- a/std/math/acosh.zig
+++ b/std/math/acosh.zig
@@ -55,11 +55,6 @@ fn acosh64(x: f64) -> f64 {
}
test "math.acosh" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
assert(acosh(f32(1.5)) == acosh32(1.5));
assert(acosh(f64(1.5)) == acosh64(1.5));
}
diff --git a/std/math/cos.zig b/std/math/cos.zig
index a65ea1ace80835ae2d46cd41c7108b47f22b4fb8..88a0193db1654f8e2e17a8a52afaf9fe9f941481 100644
--- a/std/math/cos.zig
+++ b/std/math/cos.zig
@@ -146,11 +146,6 @@ test "math.cos" {
}
test "math.cos32" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
const epsilon = 0.000001;
assert(math.approxEq(f32, cos32(0.0), 1.0, epsilon));
diff --git a/std/math/cosh.zig b/std/math/cosh.zig
index 05565d6b3ab677bddc2066b3a662bd1ce5764df4..6e57e85d141080238edf61af0304feb064ea0e62 100644
--- a/std/math/cosh.zig
+++ b/std/math/cosh.zig
@@ -81,11 +81,6 @@ fn cosh64(x: f64) -> f64 {
}
test "math.cosh" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
assert(cosh(f32(1.5)) == cosh32(1.5));
assert(cosh(f64(1.5)) == cosh64(1.5));
}
diff --git a/std/math/index.zig b/std/math/index.zig
index d925740030df1c092f5e60768b0fbe5342f4631b..07a4d9a7319e5a4786b7788b71bf6ac26ce35eff 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -359,11 +359,6 @@ pub fn divTrunc(comptime T: type, numerator: T, denominator: T) -> %T {
}
test "math.divTrunc" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
testDivTrunc();
comptime testDivTrunc();
}
@@ -389,11 +384,6 @@ pub fn divFloor(comptime T: type, numerator: T, denominator: T) -> %T {
}
test "math.divFloor" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
testDivFloor();
comptime testDivFloor();
}
@@ -423,11 +413,6 @@ pub fn divExact(comptime T: type, numerator: T, denominator: T) -> %T {
}
test "math.divExact" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
testDivExact();
comptime testDivExact();
}
diff --git a/std/math/ln.zig b/std/math/ln.zig
index c5a5c93842cccbaa0a888d2cba1be8c7c53da521..57e2ffdec80e27e6b5de9e84c6694fed46e553ed 100644
--- a/std/math/ln.zig
+++ b/std/math/ln.zig
@@ -147,11 +147,6 @@ pub fn ln_64(x_: f64) -> f64 {
}
test "math.ln" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
assert(ln(f32(0.2)) == ln_32(0.2));
assert(ln(f64(0.2)) == ln_64(0.2));
}
diff --git a/std/math/log.zig b/std/math/log.zig
index 4edb77bb1a96076c4773ebd83c0a94e445965b4d..6bed4c0da109b629a431e8f3941db0b251583c60 100644
--- a/std/math/log.zig
+++ b/std/math/log.zig
@@ -56,11 +56,6 @@ test "math.log float" {
}
test "math.log float_special" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
assert(log(f32, 2, 0.2301974) == math.log2(f32(0.2301974)));
assert(log(f32, 10, 0.2301974) == math.log10(f32(0.2301974)));
diff --git a/std/math/log10.zig b/std/math/log10.zig
index 0b6fc31aa774896d890e5b1ce4f164d3d92f7912..e27d3eb272629893ba120c576201ccedb6d05644 100644
--- a/std/math/log10.zig
+++ b/std/math/log10.zig
@@ -172,11 +172,6 @@ pub fn log10_64(x_: f64) -> f64 {
}
test "math.log10" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
assert(log10(f32(0.2)) == log10_32(0.2));
assert(log10(f64(0.2)) == log10_64(0.2));
}
diff --git a/std/math/log2.zig b/std/math/log2.zig
index a9789e47cfbe936b2fa4fd3e4232cc58ceffbd51..783975997451b02bd7b416ff8cd3d119af72deb1 100644
--- a/std/math/log2.zig
+++ b/std/math/log2.zig
@@ -170,11 +170,6 @@ pub fn log2_64(x_: f64) -> f64 {
}
test "math.log2" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
assert(log2(f32(0.2)) == log2_32(0.2));
assert(log2(f64(0.2)) == log2_64(0.2));
}
diff --git a/std/math/pow.zig b/std/math/pow.zig
index b21fb3a9213b46a5fdb84de7b63dfe0a59ae32f8..c0157ff16eee181cc56d9c05e3b405d1b5633ba5 100644
--- a/std/math/pow.zig
+++ b/std/math/pow.zig
@@ -176,12 +176,6 @@ fn isOddInteger(x: f64) -> bool {
}
test "math.pow" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
-
const epsilon = 0.000001;
assert(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon));
diff --git a/std/math/round.zig b/std/math/round.zig
index 3abee040ef74856810e60006668d86b95b14adc5..06b9e41f583762ae88a54d8925c0d79fdd7fd086 100644
--- a/std/math/round.zig
+++ b/std/math/round.zig
@@ -98,11 +98,6 @@ test "math.round" {
}
test "math.round32" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
assert(round32(1.3) == 1.0);
assert(round32(-1.3) == -1.0);
assert(round32(0.2) == 0.0);
diff --git a/std/math/sin.zig b/std/math/sin.zig
index 99008af469e1bbd12198384005e16e0deefc8d4b..5b1090e99e711bcee585a95456efc158288a4e21 100644
--- a/std/math/sin.zig
+++ b/std/math/sin.zig
@@ -150,11 +150,6 @@ test "math.sin" {
}
test "math.sin32" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
const epsilon = 0.000001;
assert(math.approxEq(f32, sin32(0.0), 0.0, epsilon));
diff --git a/std/math/sinh.zig b/std/math/sinh.zig
index ca06bc615a75b7df86423802c953c115cd61ccb2..b0057f897b996dd9c29ef08b5490b293869ab8ea 100644
--- a/std/math/sinh.zig
+++ b/std/math/sinh.zig
@@ -88,11 +88,6 @@ fn sinh64(x: f64) -> f64 {
}
test "math.sinh" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
assert(sinh(f32(1.5)) == sinh32(1.5));
assert(sinh(f64(1.5)) == sinh64(1.5));
}
diff --git a/std/math/tan.zig b/std/math/tan.zig
index e62e9b889936a7977c34156858b490481da080df..9afe102d640e782ce567d4228d8c989bc8a0780c 100644
--- a/std/math/tan.zig
+++ b/std/math/tan.zig
@@ -136,11 +136,6 @@ test "math.tan" {
}
test "math.tan32" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
const epsilon = 0.000001;
assert(math.approxEq(f32, tan32(0.0), 0.0, epsilon));
diff --git a/std/math/tanh.zig b/std/math/tanh.zig
index 813b4f4561c60ca164cdea38e2b4e5ebff0e413f..8560538cdf7fd09ba55893e4cbdb2727e0857d4a 100644
--- a/std/math/tanh.zig
+++ b/std/math/tanh.zig
@@ -112,11 +112,6 @@ fn tanh64(x: f64) -> f64 {
}
test "math.tanh" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
assert(tanh(f32(1.5)) == tanh32(1.5));
assert(tanh(f64(1.5)) == tanh64(1.5));
}
diff --git a/std/rand.zig b/std/rand.zig
index 09109cfbf7da5f167f61ae823bd8ad5cfc5aa3bc..5f4c9c201543f4ffa88777534f7f28ab2ce0842f 100644
--- a/std/rand.zig
+++ b/std/rand.zig
@@ -194,11 +194,6 @@ fn MersenneTwister(
}
test "rand float 32" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
var r = Rand.init(42);
var i: usize = 0;
while (i < 1000) : (i += 1) {
@@ -209,11 +204,6 @@ test "rand float 32" {
}
test "rand.MT19937_64" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
var rng = MT19937_64.init(rand_test.mt64_seed);
for (rand_test.mt64_data) |value| {
assert(value == rng.get());
@@ -221,11 +211,6 @@ test "rand.MT19937_64" {
}
test "rand.MT19937_32" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
var rng = MT19937_32.init(rand_test.mt32_seed);
for (rand_test.mt32_data) |value| {
assert(value == rng.get());
@@ -233,11 +218,6 @@ test "rand.MT19937_32" {
}
test "rand.Rand.range" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
var r = Rand.init(42);
testRange(&r, -4, 3);
testRange(&r, -4, -1);
diff --git a/std/sort.zig b/std/sort.zig
index 02cb9acb2e9b7077edf319c0c63b1f1ccabc8280..8554ab495aa5147fe9a2481c2f535d8c5e383f27 100644
--- a/std/sort.zig
+++ b/std/sort.zig
@@ -1020,11 +1020,6 @@ fn cmpByValue(a: &const IdAndValue, b: &const IdAndValue) -> bool {
}
test "std.sort" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
const u8cases = [][]const []const u8 {
[][]const u8{"", ""},
[][]const u8{"a", "a"},
@@ -1061,11 +1056,6 @@ test "std.sort" {
}
test "std.sort descending" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
const rev_cases = [][]const []const i32 {
[][]const i32{[]i32{}, []i32{}},
[][]const i32{[]i32{1}, []i32{1}},
@@ -1085,11 +1075,6 @@ test "std.sort descending" {
}
test "another sort case" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
var arr = []i32{ 5, 3, 1, 2, 4 };
sort(i32, arr[0..], i32asc);
@@ -1097,11 +1082,6 @@ test "another sort case" {
}
test "sort fuzz testing" {
- if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
- // TODO get this test passing
- // https://github.com/zig-lang/zig/issues/537
- return;
- }
var rng = std.rand.Rand.init(0x12345678);
const test_case_count = 10;
var i: usize = 0;
diff --git a/test/tests.zig b/test/tests.zig
index 20e1e9445992df46dbc9721fb956da5a5be38c52..4969379a6d6513ab8cb4538d2510edd983d4a13f 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -42,11 +42,6 @@ const test_targets = []TestTarget {
.arch = builtin.Arch.x86_64,
.environ = builtin.Environ.msvc,
},
- TestTarget {
- .os = builtin.Os.windows,
- .arch = builtin.Arch.i386,
- .environ = builtin.Environ.msvc,
- },
};
error TestFailed;
--
2.54.0
From f0df2cdde9181f954dc124dab004663e56c86d0f Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 14 Jan 2018 16:26:06 -0500
Subject: [PATCH 12/25] error return traces use a zig-provided function to save
binary size
---
src/all_types.hpp | 2 +
src/codegen.cpp | 110 +++++++++++++++++++++++++++++++++++-----------
2 files changed, 86 insertions(+), 26 deletions(-)
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 6476c08625f12f2ed59538435c2f338334df452b..7a3f1a9b796e0499bcf4780bfc2108edf347585d 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1574,6 +1574,8 @@ struct CodeGen {
size_t largest_err_name_len;
LLVMValueRef safety_crash_err_fn;
+ LLVMValueRef return_err_fn;
+
IrInstruction *invalid_instruction;
ConstExprValue const_void_val;
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 8b2cf2bcd4158f3aad2498fc65dc664cadb0bb84..e3462257425e5c253c96e4ee8cedf3d4cbb2c7af 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -892,7 +892,8 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace
msg_arg,
stack_trace_arg,
};
- ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, "");
+ LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, "");
+ LLVMSetTailCall(call_instruction, true);
LLVMBuildUnreachable(g->builder);
}
@@ -919,6 +920,79 @@ static LLVMValueRef get_memcpy_fn_val(CodeGen *g) {
return g->memcpy_fn_val;
}
+static LLVMValueRef get_return_err_fn(CodeGen *g) {
+ if (g->return_err_fn != nullptr)
+ return g->return_err_fn;
+
+ assert(g->err_tag_type != nullptr);
+
+ LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
+
+ LLVMTypeRef arg_types[] = {
+ // error return trace pointer
+ get_ptr_to_stack_trace_type(g)->type_ref,
+ // return address
+ ptr_u8,
+ };
+ LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
+
+ Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_return_error"), false);
+ LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
+ addLLVMFnAttr(fn_val, "cold");
+ LLVMSetLinkage(fn_val, LLVMInternalLinkage);
+ LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
+ addLLVMFnAttr(fn_val, "nounwind");
+ add_uwtable_attr(g, fn_val);
+ addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
+ addLLVMArgAttr(fn_val, (unsigned)1, "nonnull");
+ if (g->build_mode == BuildModeDebug) {
+ ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
+ ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr);
+ }
+
+ LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
+ LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
+ LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder);
+ LLVMPositionBuilderAtEnd(g->builder, entry_block);
+ ZigLLVMClearCurrentDebugLocation(g->builder);
+
+ LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
+
+ // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = return_address;
+
+ LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0);
+ size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
+ LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, "");
+ size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
+ LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)addresses_field_index, "");
+
+ LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, "");
+ LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), "");
+ LLVMValueRef address_indices[] = {
+ LLVMConstNull(usize_type_ref),
+ modded_val,
+ };
+ LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, addresses_field_ptr, address_indices, 2, "");
+
+ LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, LLVMGetParam(fn_val, 1), usize_type_ref, "");
+
+ LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, return_address, usize_type_ref, "");
+ gen_store_untyped(g, address_value, address_slot, 0, false);
+
+ // stack_trace.index += 1;
+ LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), "");
+ gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false);
+
+ // return;
+ LLVMBuildRetVoid(g->builder);
+
+ LLVMPositionBuilderAtEnd(g->builder, prev_block);
+ LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
+
+ g->return_err_fn = fn_val;
+ return fn_val;
+}
+
static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
if (g->safety_crash_err_fn != nullptr)
return g->safety_crash_err_fn;
@@ -1053,8 +1127,9 @@ static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
err_val,
err_ret_trace_val,
};
- ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified),
+ LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified),
ZigLLVM_FnInlineAuto, "");
+ LLVMSetTailCall(call_instruction, true);
LLVMBuildUnreachable(g->builder);
}
@@ -1346,36 +1421,19 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
is_err_return = true;
}
if (is_err_return) {
- LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
-
- // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = @instructionPointer();
- // stack_trace.index += 1;
-
LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError");
-
LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block);
- size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
- LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, "");
- size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
- LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, "");
- // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = @instructionPointer();
- LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, "");
- LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), "");
- LLVMValueRef address_indices[] = {
- LLVMConstNull(usize_type_ref),
- modded_val,
+ LLVMValueRef return_err_fn = get_return_err_fn(g);
+ LLVMValueRef args[] = {
+ g->cur_err_ret_trace_val,
+ block_address,
};
- LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, addresses_field_ptr, address_indices, 2, "");
- LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, block_address, usize_type_ref, "");
- gen_store_untyped(g, address_value, address_slot, 0, false);
-
- // stack_trace.index += 1;
- LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), "");
- gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false);
-
LLVMBuildBr(g->builder, return_block);
LLVMPositionBuilderAtEnd(g->builder, return_block);
+ LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 2,
+ get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
+ LLVMSetTailCall(call_instruction, true);
}
if (handle_is_ptr(return_type)) {
if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) {
--
2.54.0
From d973b40884be1c7874805c81981cac7edca5605b Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 14 Jan 2018 19:40:02 -0500
Subject: [PATCH 13/25] stack traces are a variable number of frames
---
src/codegen.cpp | 57 +++++++++++++++++++++++++++++++++++++------------
1 file changed, 43 insertions(+), 14 deletions(-)
diff --git a/src/codegen.cpp b/src/codegen.cpp
index e3462257425e5c253c96e4ee8cedf3d4cbb2c7af..96c08796158e27bb1f8ad0a584583570c0c56a64 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -22,8 +22,6 @@
#include
#include
-static const size_t stack_trace_ptr_count = 31;
-
static void init_darwin_native(CodeGen *g) {
char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET");
char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET");
@@ -958,7 +956,7 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) {
LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
- // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = return_address;
+ // stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address;
LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0);
size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
@@ -966,13 +964,21 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) {
size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)addresses_field_index, "");
+ TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
+ size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
+ LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
+ size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
+ LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
+
+ LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, "");
LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, "");
- LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), "");
+ LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, len_value, "");
LLVMValueRef address_indices[] = {
- LLVMConstNull(usize_type_ref),
modded_val,
};
- LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, addresses_field_ptr, address_indices, 2, "");
+
+ LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, "");
+ LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, "");
LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, LLVMGetParam(fn_val, 1), usize_type_ref, "");
@@ -1052,8 +1058,8 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false);
LLVMTypeRef arg_types[] = {
- g->err_tag_type->type_ref,
g->ptr_to_stack_trace_type->type_ref,
+ g->err_tag_type->type_ref,
};
LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
@@ -1077,7 +1083,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
LLVMPositionBuilderAtEnd(g->builder, entry_block);
ZigLLVMClearCurrentDebugLocation(g->builder);
- LLVMValueRef err_val = LLVMGetParam(fn_val, 0);
+ LLVMValueRef err_val = LLVMGetParam(fn_val, 1);
LLVMValueRef err_table_indices[] = {
LLVMConstNull(g->builtin_types.entry_usize->type_ref),
@@ -1107,7 +1113,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
LLVMValueRef global_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, global_slice, slice_len_index, "");
gen_store(g, full_buf_len, global_slice_len_field_ptr, u8_ptr_type);
- gen_panic(g, global_slice, LLVMGetParam(fn_val, 1));
+ gen_panic(g, global_slice, LLVMGetParam(fn_val, 0));
LLVMPositionBuilderAtEnd(g->builder, prev_block);
LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
@@ -1124,8 +1130,8 @@ static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref);
}
LLVMValueRef args[] = {
- err_val,
err_ret_trace_val,
+ err_val,
};
LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified),
ZigLLVM_FnInlineAuto, "");
@@ -4651,11 +4657,34 @@ static void do_code_gen(CodeGen *g) {
if (err_ret_trace_arg_index != UINT32_MAX) {
g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index);
} else if (fn_table_entry->calls_errorable_function) {
+ // TODO call graph analysis to find out what this number needs to be for every function
+ static const size_t stack_trace_ptr_count = 30;
+
+ TypeTableEntry *usize = g->builtin_types.entry_usize;
+ TypeTableEntry *array_type = get_array_type(g, usize, stack_trace_ptr_count);
+ LLVMValueRef err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses",
+ get_abi_alignment(g, array_type));
g->cur_err_ret_trace_val = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type));
size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, "");
- TypeTableEntry *usize = g->builtin_types.entry_usize;
gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false);
+
+ size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
+ LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, "");
+
+ TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
+ size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
+ LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
+ LLVMValueRef zero = LLVMConstNull(usize->type_ref);
+ LLVMValueRef indices[] = {zero, zero};
+ LLVMValueRef err_ret_array_val_elem0_ptr = LLVMBuildInBoundsGEP(g->builder, err_ret_array_val,
+ indices, 2, "");
+ gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr,
+ get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false));
+
+ size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
+ LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
+ gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false));
} else {
g->cur_err_ret_trace_val = nullptr;
}
@@ -5246,11 +5275,11 @@ static void define_builtin_compile_vars(CodeGen *g) {
os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
Buf *contents = buf_alloc();
- buf_appendf(contents,
+ buf_append_str(contents,
"pub const StackTrace = struct {\n"
" index: usize,\n"
- " instruction_addresses: [%" ZIG_PRI_usize "]usize,\n"
- "};\n\n", stack_trace_ptr_count);
+ " instruction_addresses: []usize,\n"
+ "};\n\n");
const char *cur_os = nullptr;
{
--
2.54.0
From 7b57454cc11371b71097967656e19f0a1736d733 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 15 Jan 2018 00:01:02 -0500
Subject: [PATCH 14/25] clean up error return tracing
* error return tracing is disabled in release-fast mode
* add @errorReturnTrace
* zig build API changes build return type from `void` to `%void`
* allow `void`, `noreturn`, and `u8` from main. closes #535
---
doc/langref.html.in | 8 +++
example/mix_o_files/build.zig | 2 +-
example/shared_library/build.zig | 2 +-
src/all_types.hpp | 7 +++
src/analyze.cpp | 12 ++---
src/codegen.cpp | 76 ++++++++++++++++++----------
src/ir.cpp | 35 +++++++++++++
src/ir_print.cpp | 7 +++
std/build.zig | 4 +-
std/debug/index.zig | 9 ++--
std/io.zig | 2 +-
std/os/index.zig | 11 +---
std/special/bootstrap.zig | 45 ++++++++++++----
std/special/build_runner.zig | 8 +--
std/special/test_runner.zig | 9 +---
test/compile_errors.zig | 18 +++----
test/debug_safety.zig | 40 +++++++--------
test/standalone/issue_339/build.zig | 2 +-
test/standalone/issue_339/test.zig | 3 +-
test/standalone/pkg_import/build.zig | 2 +-
test/standalone/use_alias/build.zig | 2 +-
test/tests.zig | 4 +-
22 files changed, 197 insertions(+), 111 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 2476790105839baae6ebbaa5f319cee02c07c2ba..94bc78095942ab135b5642eeca884c6c6d1febe2 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -142,6 +142,7 @@
@TagType
@EnumTagType
@errorName
+ @errorReturnTrace
@fence
@fieldParentPtr
@frameAddress
@@ -4412,6 +4413,13 @@ test.zig:6:2: error: found compile log statement
or all calls have a compile-time known value for err, then no
error name table will be generated.
+ @errorReturnTrace
+ @errorReturnTrace() -> ?&builtin.StackTrace
+
+ If the binary is built with error return tracing, and this function is invoked in a
+ function that calls a function with an error or error union return type, returns a
+ stack trace object. Otherwise returns `null`.
+
@fence
@fence(order: AtomicOrder)
diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig
index 391af9924a631169bdb5f482d118049bc9400dac..4bba69b0919aa265ac04484d1c309ba2d6c538c7 100644
--- a/example/mix_o_files/build.zig
+++ b/example/mix_o_files/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) {
+pub fn build(b: &Builder) -> %void {
const obj = b.addObject("base64", "base64.zig");
const exe = b.addCExecutable("test");
diff --git a/example/shared_library/build.zig b/example/shared_library/build.zig
index 9b7f3793c6f630d53a5813a7f2ab2288fce07e55..147b54401cc0de99be41870413e2213e79b6f20d 100644
--- a/example/shared_library/build.zig
+++ b/example/shared_library/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) {
+pub fn build(b: &Builder) -> %void {
const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
const exe = b.addCExecutable("test");
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 7a3f1a9b796e0499bcf4780bfc2108edf347585d..b4010976470ce978110ef5e5515c53d3a7529365 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1274,6 +1274,7 @@ enum BuiltinFnId {
BuiltinFnIdSetAlignStack,
BuiltinFnIdArgType,
BuiltinFnIdExport,
+ BuiltinFnIdErrorReturnTrace,
};
struct BuiltinFnEntry {
@@ -1499,6 +1500,7 @@ struct CodeGen {
Buf triple_str;
BuildMode build_mode;
bool is_test_build;
+ bool have_err_ret_tracing;
uint32_t target_os_index;
uint32_t target_arch_index;
uint32_t target_environ_index;
@@ -1902,6 +1904,7 @@ enum IrInstructionId {
IrInstructionIdSetAlignStack,
IrInstructionIdArgType,
IrInstructionIdExport,
+ IrInstructionIdErrorReturnTrace,
};
struct IrInstruction {
@@ -2723,6 +2726,10 @@ struct IrInstructionExport {
IrInstruction *target;
};
+struct IrInstructionErrorReturnTrace {
+ IrInstruction base;
+};
+
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index f7c0f832a2fbe063b3c474039f83497abeb753ae..8028ace28900e763968d763f7b4139ea28a54dc7 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -925,8 +925,9 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
if (!skip_debug_info) {
bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) &&
handle_is_ptr(fn_type_id->return_type);
- bool prefix_arg_error_return_trace = fn_type_id->return_type->id == TypeTableEntryIdErrorUnion ||
- fn_type_id->return_type->id == TypeTableEntryIdPureError;
+ bool prefix_arg_error_return_trace = g->have_err_ret_tracing &&
+ (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion ||
+ fn_type_id->return_type->id == TypeTableEntryIdPureError);
// +1 for maybe making the first argument the return value
// +1 for maybe last argument the error return trace
LLVMTypeRef *gen_param_types = allocate(2 + fn_type_id->param_count);
@@ -2711,13 +2712,6 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
{
if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {
g->main_fn = fn_table_entry;
- TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
- TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
- if (actual_return_type != err_void) {
- add_node_error(g, fn_proto->return_type,
- buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
- buf_ptr(&actual_return_type->name)));
- }
} else if ((import->package == g->panic_package || g->have_pub_panic) &&
buf_eql_str(&fn_table_entry->symbol_name, "panic"))
{
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 96c08796158e27bb1f8ad0a584583570c0c56a64..5113aa72754a951657bfe421ac123ce755e26822 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -404,7 +404,10 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) {
zig_unreachable();
}
-static uint32_t get_err_ret_trace_arg_index(FnTableEntry *fn_table_entry) {
+static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_entry) {
+ if (!g->have_err_ret_tracing) {
+ return UINT32_MAX;
+ }
TypeTableEntry *fn_type = fn_table_entry->type_entry;
TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdPureError) {
@@ -572,7 +575,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
}
}
- uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(fn_table_entry);
+ uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
if (err_ret_trace_arg_index != UINT32_MAX) {
addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
}
@@ -1415,31 +1418,33 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
TypeTableEntry *return_type = return_instruction->value->value.type;
- bool is_err_return = false;
- if (return_type->id == TypeTableEntryIdErrorUnion) {
- if (return_instruction->value->value.special == ConstValSpecialStatic) {
- is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr;
- } else if (return_instruction->value->value.special == ConstValSpecialRuntime) {
- is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
- // TODO: emit a branch to check if the return value is an error
+ if (g->have_err_ret_tracing) {
+ bool is_err_return = false;
+ if (return_type->id == TypeTableEntryIdErrorUnion) {
+ if (return_instruction->value->value.special == ConstValSpecialStatic) {
+ is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr;
+ } else if (return_instruction->value->value.special == ConstValSpecialRuntime) {
+ is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
+ // TODO: emit a branch to check if the return value is an error
+ }
+ } else if (return_type->id == TypeTableEntryIdPureError) {
+ is_err_return = true;
}
- } else if (return_type->id == TypeTableEntryIdPureError) {
- is_err_return = true;
- }
- if (is_err_return) {
- LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError");
- LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block);
+ if (is_err_return) {
+ LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError");
+ LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block);
- LLVMValueRef return_err_fn = get_return_err_fn(g);
- LLVMValueRef args[] = {
- g->cur_err_ret_trace_val,
- block_address,
- };
- LLVMBuildBr(g->builder, return_block);
- LLVMPositionBuilderAtEnd(g->builder, return_block);
- LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 2,
- get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
- LLVMSetTailCall(call_instruction, true);
+ LLVMValueRef return_err_fn = get_return_err_fn(g);
+ LLVMValueRef args[] = {
+ g->cur_err_ret_trace_val,
+ block_address,
+ };
+ LLVMBuildBr(g->builder, return_block);
+ LLVMPositionBuilderAtEnd(g->builder, return_block);
+ LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 2,
+ get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
+ LLVMSetTailCall(call_instruction, true);
+ }
}
if (handle_is_ptr(return_type)) {
if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) {
@@ -2475,7 +2480,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
TypeTableEntry *src_return_type = fn_type_id->return_type;
bool ret_has_bits = type_has_bits(src_return_type);
bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
- bool prefix_arg_err_ret_stack = src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError;
+ bool prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError);
size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0);
bool is_var_args = fn_type_id->is_var_args;
LLVMValueRef *gen_param_values = allocate(actual_param_count);
@@ -3031,6 +3036,16 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
return target_val;
}
+static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *executable,
+ IrInstructionErrorReturnTrace *instruction)
+{
+ TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
+ if (g->cur_err_ret_trace_val == nullptr) {
+ return LLVMConstNull(ptr_to_stack_trace_type->type_ref);
+ }
+ return g->cur_err_ret_trace_val;
+}
+
static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
switch (atomic_order) {
case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered;
@@ -3804,6 +3819,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction);
case IrInstructionIdAlignCast:
return ir_render_align_cast(g, executable, (IrInstructionAlignCast *)instruction);
+ case IrInstructionIdErrorReturnTrace:
+ return ir_render_error_return_trace(g, executable, (IrInstructionErrorReturnTrace *)instruction);
}
zig_unreachable();
}
@@ -4653,10 +4670,10 @@ static void do_code_gen(CodeGen *g) {
build_all_basic_blocks(g, fn_table_entry);
clear_debug_source_node(g);
- uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(fn_table_entry);
+ uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
if (err_ret_trace_arg_index != UINT32_MAX) {
g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index);
- } else if (fn_table_entry->calls_errorable_function) {
+ } else if (g->have_err_ret_tracing && fn_table_entry->calls_errorable_function) {
// TODO call graph analysis to find out what this number needs to be for every function
static const size_t stack_trace_ptr_count = 30;
@@ -5251,6 +5268,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1);
create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2);
create_builtin_fn(g, BuiltinFnIdExport, "export", 3);
+ create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);
}
static const char *bool_to_str(bool b) {
@@ -5553,6 +5571,8 @@ static void init(CodeGen *g) {
}
}
+ g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease;
+
define_builtin_fns(g);
define_builtin_compile_vars(g);
}
diff --git a/src/ir.cpp b/src/ir.cpp
index 5dd608083f59e7a2af5e8c21753f8e4e0352f3a3..d6844f32ba777c3f9dd01b1ecb1c51ea80322ddd 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -572,6 +572,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) {
return IrInstructionIdArgType;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace *) {
+ return IrInstructionIdErrorReturnTrace;
+}
+
template
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate(1);
@@ -2305,6 +2309,12 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s
return &instruction->base;
}
+static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node) {
+ IrInstructionErrorReturnTrace *instruction = ir_build_instruction(irb, scope, source_node);
+
+ return &instruction->base;
+}
+
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@@ -3731,6 +3741,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value);
}
+ case BuiltinFnIdErrorReturnTrace:
+ {
+ return ir_build_error_return_trace(irb, scope, node);
+ }
}
zig_unreachable();
}
@@ -9568,6 +9582,24 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
return ira->codegen->builtin_types.entry_void;
}
+static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
+ IrInstructionErrorReturnTrace *instruction)
+{
+ FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
+ TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
+ TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
+ if (fn_entry == nullptr || !fn_entry->calls_errorable_function || !ira->codegen->have_err_ret_tracing) {
+ ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+ out_val->data.x_maybe = nullptr;
+ return nullable_type;
+ }
+
+ IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
+ instruction->base.source_node);
+ ir_link_new_instruction(new_instruction, &instruction->base);
+ return nullable_type;
+}
+
static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,
IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)
{
@@ -15324,6 +15356,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction);
case IrInstructionIdExport:
return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction);
+ case IrInstructionIdErrorReturnTrace:
+ return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction);
}
zig_unreachable();
}
@@ -15507,6 +15541,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdOpaqueType:
case IrInstructionIdArgType:
case IrInstructionIdTagType:
+ case IrInstructionIdErrorReturnTrace:
return false;
case IrInstructionIdAsm:
{
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index f5aba2a45d481c8998c4b318992084def8751311..930d22f21ab83c9f2dcfaca58702e82070e4de72 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -996,6 +996,10 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {
}
}
+static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) {
+ fprintf(irp->f, "@errorReturnTrace()");
+}
+
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
@@ -1308,6 +1312,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdExport:
ir_print_export(irp, (IrInstructionExport *)instruction);
break;
+ case IrInstructionIdErrorReturnTrace:
+ ir_print_error_return_trace(irp, (IrInstructionErrorReturnTrace *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/std/build.zig b/std/build.zig
index 6984715466453c4b837f8495da116833a7920e24..5d79b00c4f8c82395e72fc010db11e84e5babda6 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -247,11 +247,11 @@ pub const Builder = struct {
defer wanted_steps.deinit();
if (step_names.len == 0) {
- wanted_steps.append(&self.default_step) catch unreachable;
+ try wanted_steps.append(&self.default_step);
} else {
for (step_names) |step_name| {
const s = try self.getTopLevelStepByName(step_name);
- wanted_steps.append(s) catch unreachable;
+ try wanted_steps.append(s);
}
}
diff --git a/std/debug/index.zig b/std/debug/index.zig
index f7f6ffa323e48ca835113acaf8640fea168d68f7..5dc7b0dbfaf3af25258fd71eb0db986c1c2225b2 100644
--- a/std/debug/index.zig
+++ b/std/debug/index.zig
@@ -56,7 +56,7 @@ pub fn dumpCurrentStackTrace() {
}
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
-pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) {
+pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) {
const stderr = getStderrStream() catch return;
const debug_info = openSelfDebugInfo(global_allocator) catch |err| {
stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
@@ -127,7 +127,7 @@ const RESET = "\x1b[0m";
error PathNotFound;
error InvalidDebugInfo;
-pub fn writeStackTrace(stack_trace: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
+pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
debug_info: &ElfStackTrace, tty_color: bool) -> %void
{
var frame_index: usize = undefined;
@@ -167,6 +167,9 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
}
fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) -> %void {
+ if (builtin.os == builtin.Os.windows) {
+ return error.UnsupportedDebugInfo;
+ }
// TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
// at compile time. I'll call it issue #313
const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
@@ -177,7 +180,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a
return;
};
const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
- if (getLineNumberInfo(debug_info, compile_unit, usize(address) - 1)) |line_info| {
+ if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
defer line_info.deinit();
try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
diff --git a/std/io.zig b/std/io.zig
index c6fd5502a2ecbe48f731349dc92f5d0dbdae196d..605553b0ea9cfafd8bee69696808ee410cd98e9a 100644
--- a/std/io.zig
+++ b/std/io.zig
@@ -224,7 +224,7 @@ pub const File = struct {
};
}
},
- else => @compileError("unsupported OS"),
+ else => @compileError("unsupported OS: " ++ @tagName(builtin.os)),
}
}
diff --git a/std/os/index.zig b/std/os/index.zig
index e33835952260fbc1464407639b856baa0c7d0fd4..fd8eb84ab41b4cff86038187cdbe238037910a98 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -148,7 +148,7 @@ pub coldcc fn abort() -> noreturn {
}
/// Exits the program cleanly with the specified status code.
-pub coldcc fn exit(status: i32) -> noreturn {
+pub coldcc fn exit(status: u8) -> noreturn {
if (builtin.link_libc) {
c.exit(status);
}
@@ -157,14 +157,7 @@ pub coldcc fn exit(status: i32) -> noreturn {
posix.exit(status);
},
Os.windows => {
- // Map a possibly negative status code to a non-negative status for the systems default
- // integer width.
- const p_status = if (@sizeOf(c_uint) < @sizeOf(u32))
- @truncate(c_uint, @bitCast(u32, status))
- else
- c_uint(@bitCast(u32, status));
-
- windows.ExitProcess(p_status);
+ windows.ExitProcess(status);
},
else => @compileError("Unsupported OS"),
}
diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig
index 55d28a2d0e07ab511a63e3c2876ad8a92d5a04ad..77bb7316a92774af918bf132793c35a073ea8560 100644
--- a/std/special/bootstrap.zig
+++ b/std/special/bootstrap.zig
@@ -21,8 +21,7 @@ comptime {
}
extern fn zenMain() -> noreturn {
- root.main() catch std.os.posix.exit(1);
- std.os.posix.exit(0);
+ std.os.posix.exit(callMain());
}
nakedcc fn _start() -> noreturn {
@@ -43,29 +42,55 @@ nakedcc fn _start() -> noreturn {
extern fn WinMainCRTStartup() -> noreturn {
@setAlignStack(16);
- root.main() catch std.os.windows.ExitProcess(1);
- std.os.windows.ExitProcess(0);
+ std.os.windows.ExitProcess(callMain());
}
fn posixCallMainAndExit() -> noreturn {
const argc = *argc_ptr;
const argv = @ptrCast(&&u8, &argc_ptr[1]);
const envp = @ptrCast(&?&u8, &argv[argc + 1]);
- callMain(argc, argv, envp) catch std.os.posix.exit(1);
- std.os.posix.exit(0);
+ std.os.posix.exit(callMainWithArgs(argc, argv, envp));
}
-fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void {
+fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) -> u8 {
std.os.ArgIteratorPosix.raw = argv[0..argc];
var env_count: usize = 0;
while (envp[env_count] != null) : (env_count += 1) {}
std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count];
- return root.main();
+ return callMain();
}
extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 {
- callMain(usize(c_argc), c_argv, c_envp) catch return 1;
- return 0;
+ return callMainWithArgs(usize(c_argc), c_argv, c_envp);
+}
+
+fn callMain() -> u8 {
+ switch (@typeId(@typeOf(root.main).ReturnType)) {
+ builtin.TypeId.NoReturn => {
+ root.main();
+ },
+ builtin.TypeId.Void => {
+ root.main();
+ return 0;
+ },
+ builtin.TypeId.Int => {
+ if (@typeOf(root.main).ReturnType.bit_count != 8) {
+ @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
+ }
+ return root.main();
+ },
+ builtin.TypeId.ErrorUnion => {
+ root.main() catch |err| {
+ std.debug.warn("error: {}\n", @errorName(err));
+ if (@errorReturnTrace()) |trace| {
+ std.debug.dumpStackTrace(trace);
+ }
+ return 1;
+ };
+ return 0;
+ },
+ else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"),
+ }
}
diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig
index b293a1e64db7d30756d985e02a595bbb53b8b33e..4722aff07abcda01009f8016c802c25e28089557 100644
--- a/std/special/build_runner.zig
+++ b/std/special/build_runner.zig
@@ -14,7 +14,7 @@ pub fn main() -> %void {
var arg_it = os.args();
// TODO use a more general purpose allocator here
- var inc_allocator = std.heap.IncrementingAllocator.init(40 * 1024 * 1024) catch unreachable;
+ var inc_allocator = try std.heap.IncrementingAllocator.init(40 * 1024 * 1024);
defer inc_allocator.deinit();
const allocator = &inc_allocator.allocator;
@@ -107,12 +107,12 @@ pub fn main() -> %void {
return usageAndErr(&builder, false, try stderr_stream);
}
} else {
- targets.append(arg) catch unreachable;
+ try targets.append(arg);
}
}
builder.setInstallPrefix(prefix);
- root.build(&builder) catch unreachable;
+ try root.build(&builder);
if (builder.validateUserInputDidItFail())
return usageAndErr(&builder, true, try stderr_stream);
@@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
// run the build script to collect the options
if (!already_ran_build) {
builder.setInstallPrefix(null);
- root.build(builder) catch unreachable;
+ try root.build(builder);
}
// This usage text has to be synchronized with src/main.cpp
diff --git a/std/special/test_runner.zig b/std/special/test_runner.zig
index 027251c52c9521465832d6717e4af542f75c1cdd..c5c0bad40c88a43d0e41b92877ce6d3999d86258 100644
--- a/std/special/test_runner.zig
+++ b/std/special/test_runner.zig
@@ -8,14 +8,7 @@ pub fn main() -> %void {
for (test_fn_list) |test_fn, i| {
warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
- if (builtin.is_test) {
- test_fn.func() catch unreachable;
- } else {
- test_fn.func() catch |err| {
- warn("{}\n", err);
- return err;
- };
- }
+ try test_fn.func();
warn("OK\n");
}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 005fe55e413aaa9facde4ebf2dbb992485b4ed7a..6b2ea545edd66a106b1ad2ca5186aa5f4a4cfc1e 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,15 @@
const tests = @import("tests.zig");
pub fn addCases(cases: &tests.CompileErrorContext) {
+ cases.add("wrong return type for main",
+ \\pub fn main() -> f32 { }
+ , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
+
+ cases.add("double ?? on main return value",
+ \\pub fn main() -> ??void {
+ \\}
+ , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
+
cases.add("bad identifier in function with struct defined inside function which references local const",
\\export fn entry() {
\\ const BlockKind = u32;
@@ -1059,15 +1068,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
,
".tmp_source.zig:2:5: error: expected type 'void', found 'error'");
- cases.add("wrong return type for main",
- \\pub fn main() { }
- , ".tmp_source.zig:1:15: error: expected return type of main to be '%void', instead is 'void'");
-
- cases.add("double ?? on main return value",
- \\pub fn main() -> ??void {
- \\}
- , ".tmp_source.zig:1:18: error: expected return type of main to be '%void', instead is '??void'");
-
cases.add("invalid pointer for var type",
\\extern fn ext() -> usize;
\\var bytes: [ext()]u8 = undefined;
diff --git a/test/debug_safety.zig b/test/debug_safety.zig
index fde5b061eec6fa2aa43433eed8f7728e414fa1cd..b32ffb34f0fb02bfff1bd1c40632e085fde322a4 100644
--- a/test/debug_safety.zig
+++ b/test/debug_safety.zig
@@ -2,7 +2,7 @@ const tests = @import("tests.zig");
pub fn addCases(cases: &tests.CompareOutputContext) {
cases.addDebugSafety("calling panic",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\pub fn main() -> %void {
@@ -11,7 +11,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("out of bounds slice access",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\pub fn main() -> %void {
@@ -25,7 +25,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer addition overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -39,7 +39,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer subtraction overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -53,7 +53,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer multiplication overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -67,7 +67,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer negation overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -81,7 +81,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("signed integer division overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -95,7 +95,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("signed shift left overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -109,7 +109,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("unsigned shift left overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -123,7 +123,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("signed shift right overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -137,7 +137,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("unsigned shift right overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -151,7 +151,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer division by zero",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -164,7 +164,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("exact division failure",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -178,7 +178,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("cast []u8 to bigger slice of wrong size",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -192,7 +192,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("value does not fit in shortening cast",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -206,7 +206,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("signed integer not fitting in cast to unsigned integer",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -220,7 +220,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("unwrap error",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) {
\\ @import("std").os.exit(126); // good
\\ }
@@ -236,7 +236,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("cast integer to error and no code matches",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\pub fn main() -> %void {
@@ -248,7 +248,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("@alignCast misaligned",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Wrong;
@@ -265,7 +265,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("bad union field access",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\
diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig
index 50fa10c5934706f61d9588d9760140bf68d18d20..b7b288f7a1741b907d41a3f713ed88e034a9f614 100644
--- a/test/standalone/issue_339/build.zig
+++ b/test/standalone/issue_339/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) {
+pub fn build(b: &Builder) -> %void {
const obj = b.addObject("test", "test.zig");
const test_step = b.step("test", "Test the program");
diff --git a/test/standalone/issue_339/test.zig b/test/standalone/issue_339/test.zig
index e0d6c3f3e51fb1589e8246634417bbdca51e8e50..dd97ffa9b1ef27b6633b2aa1343db5e98fea8b76 100644
--- a/test/standalone/issue_339/test.zig
+++ b/test/standalone/issue_339/test.zig
@@ -1,4 +1,5 @@
-pub fn panic(msg: []const u8) -> noreturn { @breakpoint(); while (true) {} }
+const StackTrace = @import("builtin").StackTrace;
+pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) -> noreturn { @breakpoint(); while (true) {} }
fn bar() -> %void {}
diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig
index 044787dd4d3f4e5cc7df79ef7d5a914231114b10..f40011d834bdfefce7e10336b93d59751d6cfc33 100644
--- a/test/standalone/pkg_import/build.zig
+++ b/test/standalone/pkg_import/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) {
+pub fn build(b: &Builder) -> %void {
const exe = b.addExecutable("test", "test.zig");
exe.addPackagePath("my_pkg", "pkg.zig");
diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig
index 03398f1a4114d92bdfd962d62dd6b5e8f874b15f..beca6bc3049171dc2367cd34866d7c01af3c34de 100644
--- a/test/standalone/use_alias/build.zig
+++ b/test/standalone/use_alias/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) {
+pub fn build(b: &Builder) -> %void {
b.addCIncludePath(".");
const main = b.addTest("main.zig");
diff --git a/test/tests.zig b/test/tests.zig
index 20e1e9445992df46dbc9721fb956da5a5be38c52..5fd995f0346fa8976a6ddfea003d8a0399103ae8 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -50,6 +50,7 @@ const test_targets = []TestTarget {
};
error TestFailed;
+error CompilationIncorrectlySucceeded;
const max_stdout_size = 1 * 1024 * 1024; // 1 MB
@@ -607,8 +608,7 @@ pub const CompileErrorContext = struct {
switch (term) {
Term.Exited => |code| {
if (code == 0) {
- warn("Compilation incorrectly succeeded\n");
- return error.TestFailed;
+ return error.CompilationIncorrectlySucceeded;
}
},
else => {
--
2.54.0
From c9ac607bd3bf2cc1cfb941e54e6bcd53f2fcc59d Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 15 Jan 2018 00:14:14 -0500
Subject: [PATCH 15/25] add builtin.have_error_return_tracing
---
src/codegen.cpp | 1 +
std/special/panic.zig | 10 ++++++----
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 5113aa72754a951657bfe421ac123ce755e26822..c72bb07c8bcc4ac8d3e29c6259a0d4995fe0d68c 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -5444,6 +5444,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt);
buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode));
buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr));
+ buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing));
buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n");
diff --git a/std/special/panic.zig b/std/special/panic.zig
index 690c4afa1c814d5f4718370ad111548160d4e809..d26166fdace09d075167d29477af313502f1ca03 100644
--- a/std/special/panic.zig
+++ b/std/special/panic.zig
@@ -13,10 +13,12 @@ pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -
while (true) {}
},
else => {
- if (error_return_trace) |trace| {
- std.debug.warn("{}\n", msg);
- std.debug.dumpStackTrace(trace);
- @import("std").debug.panic("");
+ if (builtin.have_error_return_tracing) {
+ if (error_return_trace) |trace| {
+ std.debug.warn("{}\n", msg);
+ std.debug.dumpStackTrace(trace);
+ @import("std").debug.panic("");
+ }
}
@import("std").debug.panic("{}", msg);
},
--
2.54.0
From 4cf86b4a94adf0a27d0c536746f1703ddf1fd0df Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Mon, 15 Jan 2018 23:14:13 +1300
Subject: [PATCH 16/25] Add Blake2X hash functions
The truncated output variants currently are dependent on a more complete
bigint implementation in the compiler.
---
CMakeLists.txt | 1 +
std/crypto/blake2x.zig | 439 +++++++++++++++++++++++++++++++++++++++++
std/crypto/index.zig | 7 +
3 files changed, 447 insertions(+)
create mode 100644 std/crypto/blake2x.zig
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 636c215f063e8f7e519211a3ea431bb9b2df51a0..b93951e4e620bfb32abdf12e1c2c54e20507857d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -368,6 +368,7 @@ set(ZIG_STD_FILES
"crypto/md5.zig"
"crypto/sha1.zig"
"crypto/sha2.zig"
+ "crypto/blake2x.zig"
"cstr.zig"
"debug/failing_allocator.zig"
"debug/index.zig"
diff --git a/std/crypto/blake2x.zig b/std/crypto/blake2x.zig
new file mode 100644
index 0000000000000000000000000000000000000000..67ef00610b2370501e971958910aa4da79c860d7
--- /dev/null
+++ b/std/crypto/blake2x.zig
@@ -0,0 +1,439 @@
+const std = @import("std");
+const mem = std.mem;
+const math = std.math;
+const debug = std.debug;
+
+const RoundParam = struct {
+ a: usize, b: usize, c: usize, d: usize, x: usize, y: usize,
+};
+
+fn Rp(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) -> RoundParam {
+ return RoundParam { .a = a, .b = b, .c = c, .d = d, .x = x, .y = y, };
+}
+
+/////////////////////
+// Blake2s
+
+pub const Blake2s224 = Blake2s(224);
+pub const Blake2s256 = Blake2s(256);
+
+fn Blake2s(comptime out_len: usize) -> type { return struct {
+ const Self = this;
+ const ReturnType = @IntType(false, out_len);
+
+ const iv = [8]u32 {
+ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
+ 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
+ };
+
+ const sigma = [10][16]u8 {
+ []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
+ []const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
+ []const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
+ []const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
+ []const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
+ []const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
+ []const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
+ []const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
+ []const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
+ };
+
+ h: [8]u32,
+ t: u64,
+ // Streaming cache
+ buf: [64]u8,
+ buf_len: u8,
+
+ pub fn init() -> Self {
+ debug.assert(8 <= out_len and out_len <= 512);
+
+ var s: Self = undefined;
+ s.reset();
+ return s;
+ }
+
+ pub fn reset(d: &Self) {
+ mem.copy(u32, d.h[0..], iv[0..]);
+
+ // No key plus default parameters
+ d.h[0] ^= 0x01010000 ^ u32(out_len >> 3);
+ d.t = 0;
+ d.buf_len = 0;
+ }
+
+ pub fn hash(b: []const u8) -> ReturnType {
+ var d = Self.init();
+ d.update(b);
+ return d.final();
+ }
+
+ pub fn update(d: &Self, b: []const u8) {
+ var off: usize = 0;
+
+ // Partial buffer exists from previous update. Copy into buffer then hash.
+ if (d.buf_len != 0 and d.buf_len + b.len > 64) {
+ off += 64 - d.buf_len;
+ mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
+ d.t += 64;
+ d.round(d.buf[0..], false);
+ d.buf_len = 0;
+ }
+
+ // Full middle blocks.
+ while (off + 64 < b.len) : (off += 64) {
+ d.t += 64;
+ d.round(b[off..off + 64], false);
+ }
+
+ // Copy any remainder for next pass.
+ mem.copy(u8, d.buf[d.buf_len..], b[off..]);
+ d.buf_len += u8(b[off..].len);
+ }
+
+ pub fn final(d: &Self) -> ReturnType {
+ mem.set(u8, d.buf[d.buf_len..], 0);
+ d.t += d.buf_len;
+ d.round(d.buf[0..], true);
+
+ const rr = d.h[0 .. out_len / 32];
+
+ // NOTE: mem.readIntLE or equivalent would be useful here.
+ var j: u8 = 0;
+ var r: ReturnType = 0;
+ for (rr) |p| {
+ r |= ReturnType(p) << j;
+ j +%= 32;
+ }
+
+ return std.endian.swapIfLe(ReturnType, r);
+ }
+
+ fn round(d: &Self, b: []const u8, last: bool) {
+ debug.assert(b.len == 64);
+
+ var m: [16]u32 = undefined;
+ var v: [16]u32 = undefined;
+
+ for (m) |*r, i| {
+ *r = mem.readIntLE(u32, b[4*i .. 4*i + 4]);
+ }
+
+ var k: usize = 0;
+ while (k < 8) : (k += 1) {
+ v[k] = d.h[k];
+ v[k+8] = iv[k];
+ }
+
+ v[12] ^= @truncate(u32, d.t);
+ v[13] ^= u32(d.t >> 32);
+ if (last) v[14] = ~v[14];
+
+ const rounds = comptime []RoundParam {
+ Rp(0, 4, 8, 12, 0, 1),
+ Rp(1, 5, 9, 13, 2, 3),
+ Rp(2, 6, 10, 14, 4, 5),
+ Rp(3, 7, 11, 15, 6, 7),
+ Rp(0, 5, 10, 15, 8, 9),
+ Rp(1, 6, 11, 12, 10, 11),
+ Rp(2, 7, 8, 13, 12, 13),
+ Rp(3, 4, 9, 14, 14, 15),
+ };
+
+ comptime var j: usize = 0;
+ inline while (j < 10) : (j += 1) {
+ inline for (rounds) |r| {
+ v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]];
+ v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(16));
+ v[r.c] = v[r.c] +% v[r.d];
+ v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(12));
+ v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]];
+ v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(8));
+ v[r.c] = v[r.c] +% v[r.d];
+ v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(7));
+ }
+ }
+
+ for (d.h) |*r, i| {
+ *r ^= v[i] ^ v[i + 8];
+ }
+ }
+};}
+
+// TODO: bigint rem >1 digits for 224 integer output.
+//
+// test "blake2s224 single" {
+// const hash1 = 0xa847d26c2f966c5c4cc222b174918a56037cdee34b3f872f;
+// debug.assert(hash1 == Blake2s224.hash(""));
+//
+// const hash2 = 0x1e2ed10fcdbc46e0ab3ea3f268a6c288083ae04e3d63a8de;
+// debug.assert(hash2 == Blake2s224.hash("abc"));
+//
+// const hash3 = 0xe486adf7b22d2944b434ae78ae64720c16ccf0479dab072d;
+// debug.assert(hash3 == Blake2s224.hash("The quick brown fox jumps over the lazy dog"));
+// }
+//
+// test "blake2s224 streaming" {
+// var h = Blake2s224.init();
+//
+// const hash1 = 0xa847d26c2f966c5c4cc222b174918a56037cdee34b3f872f;
+// debug.assert(hash1 == h.final());
+//
+// const hash2 = 0x1e2ed10fcdbc46e0ab3ea3f268a6c288083ae04e3d63a8de;
+//
+// h.reset();
+// h.update("abc");
+// debug.assert(hash2 == h.final());
+//
+// h.reset();
+// h.update("a");
+// h.update("b");
+// h.update("c");
+// debug.assert(hash2 == h.final());
+// }
+
+test "blake2s256 single" {
+ const hash1 = 0x69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9;
+ debug.assert(hash1 == Blake2s256.hash(""));
+
+ const hash2 = 0x508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982;
+ debug.assert(hash2 == Blake2s256.hash("abc"));
+
+ const hash3 = 0x606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812;
+ debug.assert(hash3 == Blake2s256.hash("The quick brown fox jumps over the lazy dog"));
+}
+
+test "blake2s256 streaming" {
+ var h = Blake2s256.init();
+
+ const hash1 = 0x69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9;
+ debug.assert(hash1 == h.final());
+
+ const hash2 = 0x508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982;
+
+ h.reset();
+ h.update("abc");
+ debug.assert(hash2 == h.final());
+
+ h.reset();
+ h.update("a");
+ h.update("b");
+ h.update("c");
+ debug.assert(hash2 == h.final());
+}
+
+
+/////////////////////
+// Blake2b
+
+pub const Blake2b384 = Blake2b(384);
+pub const Blake2b512 = Blake2b(512);
+
+fn Blake2b(comptime out_len: usize) -> type { return struct {
+ const Self = this;
+ const ReturnType = @IntType(false, out_len);
+ const u9 = @IntType(false, 9);
+
+ const iv = [8]u64 {
+ 0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
+ 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
+ 0x510e527fade682d1, 0x9b05688c2b3e6c1f,
+ 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
+ };
+
+ const sigma = [12][16]u8 {
+ []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
+ []const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
+ []const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
+ []const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
+ []const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
+ []const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
+ []const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
+ []const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
+ []const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 },
+ []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
+ };
+
+ h: [8]u64,
+ t: u128,
+ // Streaming cache
+ buf: [128]u8,
+ buf_len: u8,
+
+ pub fn init() -> Self {
+ debug.assert(8 <= out_len and out_len <= 512);
+
+ var s: Self = undefined;
+ s.reset();
+ return s;
+ }
+
+ pub fn reset(d: &Self) {
+ mem.copy(u64, d.h[0..], iv[0..]);
+
+ // No key plus default parameters
+ d.h[0] ^= 0x01010000 ^ (out_len >> 3);
+ d.t = 0;
+ d.buf_len = 0;
+ }
+
+ pub fn hash(b: []const u8) -> ReturnType {
+ var d = Self.init();
+ d.update(b);
+ return d.final();
+ }
+
+ pub fn update(d: &Self, b: []const u8) {
+ var off: usize = 0;
+
+ // Partial buffer exists from previous update. Copy into buffer then hash.
+ if (d.buf_len != 0 and d.buf_len + b.len > 128) {
+ off += 128 - d.buf_len;
+ mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
+ d.t += 128;
+ d.round(d.buf[0..], false);
+ d.buf_len = 0;
+ }
+
+ // Full middle blocks.
+ while (off + 128 < b.len) : (off += 128) {
+ d.t += 128;
+ d.round(b[off..off + 128], false);
+ }
+
+ // Copy any remainder for next pass.
+ mem.copy(u8, d.buf[d.buf_len..], b[off..]);
+ d.buf_len += u8(b[off..].len);
+ }
+
+ pub fn final(d: &Self) -> ReturnType {
+ mem.set(u8, d.buf[d.buf_len..], 0);
+ d.t += d.buf_len;
+ d.round(d.buf[0..], true);
+
+ const rr = d.h[0 .. out_len / 64];
+
+ var j: u9 = 0;
+ var r: ReturnType = 0;
+ for (rr) |p| {
+ r |= ReturnType(p) << j;
+ j +%= 64;
+ }
+
+ return std.endian.swapIfLe(ReturnType, r);
+ }
+
+ fn round(d: &Self, b: []const u8, last: bool) {
+ debug.assert(b.len == 128);
+
+ var m: [16]u64 = undefined;
+ var v: [16]u64 = undefined;
+
+ for (m) |*r, i| {
+ *r = mem.readIntLE(u64, b[8*i .. 8*i + 8]);
+ }
+
+ var k: usize = 0;
+ while (k < 8) : (k += 1) {
+ v[k] = d.h[k];
+ v[k+8] = iv[k];
+ }
+
+ v[12] ^= @truncate(u64, d.t);
+ v[13] ^= u64(d.t >> 64);
+ if (last) v[14] = ~v[14];
+
+ const rounds = comptime []RoundParam {
+ Rp(0, 4, 8, 12, 0, 1),
+ Rp(1, 5, 9, 13, 2, 3),
+ Rp(2, 6, 10, 14, 4, 5),
+ Rp(3, 7, 11, 15, 6, 7),
+ Rp(0, 5, 10, 15, 8, 9),
+ Rp(1, 6, 11, 12, 10, 11),
+ Rp(2, 7, 8, 13, 12, 13),
+ Rp(3, 4, 9, 14, 14, 15),
+ };
+
+ comptime var j: usize = 0;
+ inline while (j < 12) : (j += 1) {
+ inline for (rounds) |r| {
+ v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]];
+ v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(32));
+ v[r.c] = v[r.c] +% v[r.d];
+ v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(24));
+ v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]];
+ v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(16));
+ v[r.c] = v[r.c] +% v[r.d];
+ v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(63));
+ }
+ }
+
+ for (d.h) |*r, i| {
+ *r ^= v[i] ^ v[i + 8];
+ }
+ }
+};}
+
+// TODO: bigint rem >1 digits for 384 integer output.
+
+// test "blake2b384 single" {
+// const hash1 = 0xb32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100;
+// debug.assert(hash1 == Blake2b384.hash(""));
+//
+// const hash2 = 0x6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4;
+// debug.assert(hash2 == Blake2b384.hash("abc"));
+//
+// const hash3 = 0xb7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d;
+// debug.assert(hash3 == Blake2b384.hash("The quick brown fox jumps over the lazy dog"));
+// }
+//
+// test "blake2b384 streaming" {
+// var h = Blake2b384.init();
+//
+// const hash1 = 0xb32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100;
+// debug.assert(hash1 == h.final());
+//
+// const hash2 = 0x6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4;
+//
+// h.reset();
+// h.update("abc");
+// debug.assert(hash2 == h.final());
+//
+// h.reset();
+// h.update("a");
+// h.update("b");
+// h.update("c");
+// debug.assert(hash2 == h.final());
+// }
+
+test "blake2b512 single" {
+ const hash1 = 0x786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce;
+ debug.assert(hash1 == Blake2b512.hash(""));
+
+ const hash2 = 0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923;
+ debug.assert(hash2 == Blake2b512.hash("abc"));
+
+ const hash3 = 0xa8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918;
+ debug.assert(hash3 == Blake2b512.hash("The quick brown fox jumps over the lazy dog"));
+}
+
+test "blake2b512 streaming" {
+ var h = Blake2b512.init();
+
+ const hash1 = 0x786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce;
+ debug.assert(hash1 == h.final());
+
+ const hash2 = 0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923;
+
+ h.reset();
+ h.update("abc");
+ debug.assert(hash2 == h.final());
+
+ h.reset();
+ h.update("a");
+ h.update("b");
+ h.update("c");
+ debug.assert(hash2 == h.final());
+}
diff --git a/std/crypto/index.zig b/std/crypto/index.zig
index 27dd467bd0db9d283f7421e3e60f0876df69ee65..a63984e3d5dc286ff7a1be18c46e94fe5bb40f04 100644
--- a/std/crypto/index.zig
+++ b/std/crypto/index.zig
@@ -7,8 +7,15 @@ pub const Sha256 = sha2.Sha256;
pub const Sha384 = sha2.Sha384;
pub const Sha512 = sha2.Sha512;
+const blake2x = @import("blake2x.zig");
+pub const Blake2s224 = blake2x.Blake2s224;
+pub const Blake2s256 = blake2x.Blake2s256;
+pub const Blake2b384 = blake2x.Blake2b384;
+pub const Blake2b512 = blake2x.Blake2b512;
+
test "crypto" {
_ = @import("md5.zig");
_ = @import("sha1.zig");
_ = @import("sha2.zig");
+ _ = @import("blake2x.zig");
}
--
2.54.0
From 6ec9933fd80013104982debab9fbff1463582f19 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 15 Jan 2018 16:26:13 -0500
Subject: [PATCH 17/25] fix getting debug info twice in default panic handler
---
std/debug/index.zig | 29 +++++++++++++++++++++++++++--
std/special/panic.zig | 8 ++------
2 files changed, 29 insertions(+), 8 deletions(-)
diff --git a/std/debug/index.zig b/std/debug/index.zig
index 5dc7b0dbfaf3af25258fd71eb0db986c1c2225b2..87e81ab8c2fb008ec120336317dd3bbf29959783 100644
--- a/std/debug/index.zig
+++ b/std/debug/index.zig
@@ -41,10 +41,21 @@ fn getStderrStream() -> %&io.OutStream {
}
}
+var self_debug_info: ?&ElfStackTrace = null;
+pub fn getSelfDebugInfo() -> %&ElfStackTrace {
+ if (self_debug_info) |info| {
+ return info;
+ } else {
+ const info = try openSelfDebugInfo(global_allocator);
+ self_debug_info = info;
+ return info;
+ }
+}
+
/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
pub fn dumpCurrentStackTrace() {
const stderr = getStderrStream() catch return;
- const debug_info = openSelfDebugInfo(global_allocator) catch |err| {
+ const debug_info = getSelfDebugInfo() catch |err| {
stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
return;
};
@@ -58,7 +69,7 @@ pub fn dumpCurrentStackTrace() {
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) {
const stderr = getStderrStream() catch return;
- const debug_info = openSelfDebugInfo(global_allocator) catch |err| {
+ const debug_info = getSelfDebugInfo() catch |err| {
stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
return;
};
@@ -119,6 +130,20 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
os.abort();
}
+pub fn panicWithTrace(trace: &const builtin.StackTrace, comptime format: []const u8, args: ...) -> noreturn {
+ if (panicking) {
+ os.abort();
+ } else {
+ panicking = true;
+ }
+ const stderr = getStderrStream() catch os.abort();
+ stderr.print(format ++ "\n", args) catch os.abort();
+ dumpStackTrace(trace);
+ dumpCurrentStackTrace();
+
+ os.abort();
+}
+
const GREEN = "\x1b[32;1m";
const WHITE = "\x1b[37;1m";
const DIM = "\x1b[2m";
diff --git a/std/special/panic.zig b/std/special/panic.zig
index d26166fdace09d075167d29477af313502f1ca03..1b22658c7ff276c66f6688d734839372493956c6 100644
--- a/std/special/panic.zig
+++ b/std/special/panic.zig
@@ -13,12 +13,8 @@ pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -
while (true) {}
},
else => {
- if (builtin.have_error_return_tracing) {
- if (error_return_trace) |trace| {
- std.debug.warn("{}\n", msg);
- std.debug.dumpStackTrace(trace);
- @import("std").debug.panic("");
- }
+ if (error_return_trace) |trace| {
+ @import("std").debug.panicWithTrace(trace, "{}", msg);
}
@import("std").debug.panic("{}", msg);
},
--
2.54.0
From 92fc5947fc18ea077f0e02bc4758e9050270e4bb Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 15 Jan 2018 20:44:21 -0500
Subject: [PATCH 18/25] fix compiler crash related to @alignOf
---
src/analyze.cpp | 1 +
src/ir.cpp | 4 +-
test/build_examples.zig | 1 +
test/standalone/brace_expansion/build.zig | 9 +
test/standalone/brace_expansion/main.zig | 254 ++++++++++++++++++++++
5 files changed, 267 insertions(+), 2 deletions(-)
create mode 100644 test/standalone/brace_expansion/build.zig
create mode 100644 test/standalone/brace_expansion/main.zig
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 8028ace28900e763968d763f7b4139ea28a54dc7..9da848501445587debf9df35a08b50090f8d9cfc 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -2231,6 +2231,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
// is a pointer to this very struct, or a function pointer with parameters that
// reference such a type.
union_type->data.unionation.zero_bits_known = true;
+ union_type->data.unionation.zero_bits_loop_flag = false;
if (union_type->data.unionation.abi_alignment == 0) {
if (union_type->data.unionation.layout == ContainerLayoutPacked) {
union_type->data.unionation.abi_alignment = 1;
diff --git a/src/ir.cpp b/src/ir.cpp
index d6844f32ba777c3f9dd01b1ecb1c51ea80322ddd..4dd022c09fcf9f5ebe3deffe013123b87537a1ee 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -9858,7 +9858,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
if (fn_proto_node->data.fn_proto.is_var_args) {
ir_add_error(ira, &call_instruction->base,
- buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/andrewrk/zig/issues/313"));
+ buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/zig-lang/zig/issues/313"));
return ira->codegen->builtin_types.entry_invalid;
}
@@ -14011,7 +14011,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
- ensure_complete_type(ira->codegen, type_entry);
+ type_ensure_zero_bits_known(ira->codegen, type_entry);
if (type_is_invalid(type_entry))
return ira->codegen->builtin_types.entry_invalid;
diff --git a/test/build_examples.zig b/test/build_examples.zig
index 29a15fd5150098b07d15b26de4ce9e821e49af13..666dac8df161aadb5f85b7cd7946edc3ac2eae3c 100644
--- a/test/build_examples.zig
+++ b/test/build_examples.zig
@@ -16,4 +16,5 @@ pub fn addCases(cases: &tests.BuildExamplesContext) {
cases.addBuildFile("test/standalone/issue_339/build.zig");
cases.addBuildFile("test/standalone/pkg_import/build.zig");
cases.addBuildFile("test/standalone/use_alias/build.zig");
+ cases.addBuildFile("test/standalone/brace_expansion/build.zig");
}
diff --git a/test/standalone/brace_expansion/build.zig b/test/standalone/brace_expansion/build.zig
new file mode 100644
index 0000000000000000000000000000000000000000..d170b50fe0d5f20ce09fd4805213cd2dde8f0a85
--- /dev/null
+++ b/test/standalone/brace_expansion/build.zig
@@ -0,0 +1,9 @@
+const Builder = @import("std").build.Builder;
+
+pub fn build(b: &Builder) -> %void {
+ const main = b.addTest("main.zig");
+ main.setBuildMode(b.standardReleaseOptions());
+
+ const test_step = b.step("test", "Test it");
+ test_step.dependOn(&main.step);
+}
diff --git a/test/standalone/brace_expansion/main.zig b/test/standalone/brace_expansion/main.zig
new file mode 100644
index 0000000000000000000000000000000000000000..7b03e230f74b840781ecd7f102cc59f00a3f5c08
--- /dev/null
+++ b/test/standalone/brace_expansion/main.zig
@@ -0,0 +1,254 @@
+const std = @import("std");
+const io = std.io;
+const mem = std.mem;
+const debug = std.debug;
+const assert = debug.assert;
+const Buffer = std.Buffer;
+const ArrayList = std.ArrayList;
+
+error InvalidInput;
+error OutOfMem;
+
+const Token = union(enum) {
+ Word: []const u8,
+ OpenBrace,
+ CloseBrace,
+ Comma,
+ Eof,
+};
+
+var global_allocator: &mem.Allocator = undefined;
+
+fn tokenize(input:[] const u8) -> %ArrayList(Token) {
+ const State = enum {
+ Start,
+ Word,
+ };
+
+ var token_list = ArrayList(Token).init(global_allocator);
+ var tok_begin: usize = undefined;
+ var state = State.Start;
+
+ for (input) |b, i| {
+ switch (state) {
+ State.Start => switch (b) {
+ 'a'...'z', 'A'...'Z' => {
+ state = State.Word;
+ tok_begin = i;
+ },
+ '{' => try token_list.append(Token.OpenBrace),
+ '}' => try token_list.append(Token.CloseBrace),
+ ',' => try token_list.append(Token.Comma),
+ else => return error.InvalidInput,
+ },
+ State.Word => switch (b) {
+ 'a'...'z', 'A'...'Z' => {},
+ '{', '}', ',' => {
+ try token_list.append(Token { .Word = input[tok_begin..i] });
+ switch (b) {
+ '{' => try token_list.append(Token.OpenBrace),
+ '}' => try token_list.append(Token.CloseBrace),
+ ',' => try token_list.append(Token.Comma),
+ else => unreachable,
+ }
+ state = State.Start;
+ },
+ else => return error.InvalidInput,
+ },
+ }
+ }
+ switch (state) {
+ State.Start => {},
+ State.Word => try token_list.append(Token {.Word = input[tok_begin..] }),
+ }
+ try token_list.append(Token.Eof);
+ return token_list;
+}
+
+const Node = union(enum) {
+ Scalar: []const u8,
+ List: ArrayList(Node),
+ Combine: []Node,
+};
+
+fn parse(tokens: &const ArrayList(Token), token_index: &usize) -> %Node {
+ const first_token = tokens.items[*token_index];
+ *token_index += 1;
+
+ const result_node = switch (first_token) {
+ Token.Word => |word| Node { .Scalar = word },
+ Token.OpenBrace => blk: {
+ var list = ArrayList(Node).init(global_allocator);
+ while (true) {
+ try list.append(try parse(tokens, token_index));
+
+ const token = tokens.items[*token_index];
+ *token_index += 1;
+
+ switch (token) {
+ Token.CloseBrace => break,
+ Token.Comma => continue,
+ else => return error.InvalidInput,
+ }
+ }
+ break :blk Node { .List = list };
+ },
+ else => return error.InvalidInput,
+ };
+
+ switch (tokens.items[*token_index]) {
+ Token.Word, Token.OpenBrace => {
+ const pair = try global_allocator.alloc(Node, 2);
+ pair[0] = result_node;
+ pair[1] = try parse(tokens, token_index);
+ return Node { .Combine = pair };
+ },
+ else => return result_node,
+ }
+}
+
+fn expandString(input: []const u8, output: &Buffer) -> %void {
+ const tokens = try tokenize(input);
+ if (tokens.len == 1) {
+ return output.resize(0);
+ }
+
+ var token_index: usize = 0;
+ const root = try parse(tokens, &token_index);
+ const last_token = tokens.items[token_index];
+ switch (last_token) {
+ Token.Eof => {},
+ else => return error.InvalidInput,
+ }
+
+ var result_list = ArrayList(Buffer).init(global_allocator);
+ defer result_list.deinit();
+
+ try expandNode(root, &result_list);
+
+ try output.resize(0);
+ for (result_list.toSliceConst()) |buf, i| {
+ if (i != 0) {
+ try output.appendByte(' ');
+ }
+ try output.append(buf.toSliceConst());
+ }
+}
+
+const ListOfBuffer0 = ArrayList(Buffer); // TODO this is working around a compiler bug, fix and delete this
+
+fn expandNode(node: &const Node, output: &ListOfBuffer0) -> %void {
+ assert(output.len == 0);
+ switch (*node) {
+ Node.Scalar => |scalar| {
+ try output.append(try Buffer.init(global_allocator, scalar));
+ },
+ Node.Combine => |pair| {
+ const a_node = pair[0];
+ const b_node = pair[1];
+
+ var child_list_a = ArrayList(Buffer).init(global_allocator);
+ try expandNode(a_node, &child_list_a);
+
+ var child_list_b = ArrayList(Buffer).init(global_allocator);
+ try expandNode(b_node, &child_list_b);
+
+ for (child_list_a.toSliceConst()) |buf_a| {
+ for (child_list_b.toSliceConst()) |buf_b| {
+ var combined_buf = try Buffer.initFromBuffer(buf_a);
+ try combined_buf.append(buf_b.toSliceConst());
+ try output.append(combined_buf);
+ }
+ }
+ },
+ Node.List => |list| {
+ for (list.toSliceConst()) |child_node| {
+ var child_list = ArrayList(Buffer).init(global_allocator);
+ try expandNode(child_node, &child_list);
+
+ for (child_list.toSliceConst()) |buf| {
+ try output.append(buf);
+ }
+ }
+ },
+ }
+}
+
+pub fn main() -> %void {
+ var stdin_file = try io.getStdIn();
+ var stdout_file = try io.getStdOut();
+
+ var inc_allocator = try std.heap.IncrementingAllocator.init(2 * 1024 * 1024);
+ defer inc_allocator.deinit();
+
+ global_allocator = &inc_allocator.allocator;
+
+ var stdin_buf = try Buffer.initSize(global_allocator, 0);
+ defer stdin_buf.deinit();
+
+ var stdin_adapter = io.FileInStream.init(&stdin_file);
+ try stdin_adapter.stream.readAllBuffer(&stdin_buf, @maxValue(usize));
+
+ var result_buf = try Buffer.initSize(global_allocator, 0);
+ defer result_buf.deinit();
+
+ try expandString(stdin_buf.toSlice(), &result_buf);
+ try stdout_file.write(result_buf.toSliceConst());
+}
+
+test "invalid inputs" {
+ global_allocator = std.debug.global_allocator;
+
+ expectError("}ABC", error.InvalidInput);
+ expectError("{ABC", error.InvalidInput);
+ expectError("}{", error.InvalidInput);
+ expectError("{}", error.InvalidInput);
+ expectError("A,B,C", error.InvalidInput);
+ expectError("{A{B,C}", error.InvalidInput);
+ expectError("{A,}", error.InvalidInput);
+
+ expectError("\n", error.InvalidInput);
+}
+
+fn expectError(test_input: []const u8, expected_err: error) {
+ var output_buf = Buffer.initSize(global_allocator, 0) catch unreachable;
+ defer output_buf.deinit();
+
+ if (expandString("}ABC", &output_buf)) {
+ unreachable;
+ } else |err| {
+ assert(expected_err == err);
+ }
+}
+
+test "valid inputs" {
+ global_allocator = std.debug.global_allocator;
+
+ expectExpansion("{x,y,z}", "x y z");
+ expectExpansion("{A,B}{x,y}", "Ax Ay Bx By");
+ expectExpansion("{A,B{x,y}}", "A Bx By");
+
+ expectExpansion("{ABC}", "ABC");
+ expectExpansion("{A,B,C}", "A B C");
+ expectExpansion("ABC", "ABC");
+
+ expectExpansion("", "");
+ expectExpansion("{A,B}{C,{x,y}}{g,h}", "ACg ACh Axg Axh Ayg Ayh BCg BCh Bxg Bxh Byg Byh");
+ expectExpansion("{A,B}{C,C{x,y}}{g,h}", "ACg ACh ACxg ACxh ACyg ACyh BCg BCh BCxg BCxh BCyg BCyh");
+ expectExpansion("{A,B}a", "Aa Ba");
+ expectExpansion("{C,{x,y}}", "C x y");
+ expectExpansion("z{C,{x,y}}", "zC zx zy");
+ expectExpansion("a{b,c{d,e{f,g}}}", "ab acd acef aceg");
+ expectExpansion("a{x,y}b", "axb ayb");
+ expectExpansion("z{{a,b}}", "za zb");
+ expectExpansion("a{b}", "ab");
+}
+
+fn expectExpansion(test_input: []const u8, expected_result: []const u8) {
+ var result = Buffer.initSize(global_allocator, 0) catch unreachable;
+ defer result.deinit();
+
+ expandString(test_input, &result) catch unreachable;
+
+ assert(mem.eql(u8, result.toSlice(), expected_result));
+}
--
2.54.0
From 84d8584c5b315c24a00fa444d758a6b78f80f4a0 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 15 Jan 2018 22:17:22 -0500
Subject: [PATCH 19/25] implement bigint div and rem
See #405
---
README.md | 5 +-
src/bigint.cpp | 442 +++++++++++++++++++++++++++++++++++++++++++-
test/cases/math.zig | 22 +++
3 files changed, 458 insertions(+), 11 deletions(-)
diff --git a/README.md b/README.md
index 6a9fa4e580e97916e0340312cb3d9abbaceb2d82..1c7d7e5258554947b4287b663c159fc68606d2c3 100644
--- a/README.md
+++ b/README.md
@@ -125,17 +125,20 @@ libc. Create demo games using Zig.
##### POSIX
+ * cmake >= 2.8.5
* gcc >= 5.0.0 or clang >= 3.6.0
- * cmake >= 2.8.5
* LLVM, Clang, LLD libraries == 5.x, compiled with the same gcc or clang version above
##### Windows
+ * cmake >= 2.8.5
* Microsoft Visual Studio 2015
* LLVM, Clang, LLD libraries == 5.x, compiled with the same MSVC version above
#### Instructions
+##### POSIX
+
If you have gcc or clang installed, you can find out what `ZIG_LIBC_LIB_DIR`,
`ZIG_LIBC_STATIC_LIB_DIR`, and `ZIG_LIBC_INCLUDE_DIR` should be set to
(example below).
diff --git a/src/bigint.cpp b/src/bigint.cpp
index f01436d232240465f1b7da3a2aec3556da6e2175..3e47a11900346c5ae0a3e74f7c251e135fce6115 100644
--- a/src/bigint.cpp
+++ b/src/bigint.cpp
@@ -12,6 +12,9 @@
#include "os.hpp"
#include "softfloat.hpp"
+#include
+#include
+
static void bigint_normalize(BigInt *dest) {
const uint64_t *digits = bigint_ptr(dest);
@@ -539,7 +542,7 @@ void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {
dest->data.digits[i] = x;
i += 1;
- if (!found_digit)
+ if (!found_digit || i >= bigger_op->digit_count)
break;
}
assert(overflow == 0);
@@ -670,19 +673,409 @@ void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t
bigint_truncate(dest, &unwrapped, bit_count, is_signed);
}
+enum ZeroBehavior {
+ /// \brief The returned value is undefined.
+ ZB_Undefined,
+ /// \brief The returned value is numeric_limits::max()
+ ZB_Max,
+ /// \brief The returned value is numeric_limits::digits
+ ZB_Width
+};
+
+template struct LeadingZerosCounter {
+ static std::size_t count(T Val, ZeroBehavior) {
+ if (!Val)
+ return std::numeric_limits::digits;
+
+ // Bisection method.
+ std::size_t ZeroBits = 0;
+ for (T Shift = std::numeric_limits::digits >> 1; Shift; Shift >>= 1) {
+ T Tmp = Val >> Shift;
+ if (Tmp)
+ Val = Tmp;
+ else
+ ZeroBits |= Shift;
+ }
+ return ZeroBits;
+ }
+};
+
+#if __GNUC__ >= 4 || defined(_MSC_VER)
+template struct LeadingZerosCounter {
+ static std::size_t count(T Val, ZeroBehavior ZB) {
+ if (ZB != ZB_Undefined && Val == 0)
+ return 32;
+
+#if defined(_MSC_VER)
+ unsigned long Index;
+ _BitScanReverse(&Index, Val);
+ return Index ^ 31;
+#else
+ return __builtin_clz(Val);
+#endif
+ }
+};
+
+#if !defined(_MSC_VER) || defined(_M_X64)
+template struct LeadingZerosCounter {
+ static std::size_t count(T Val, ZeroBehavior ZB) {
+ if (ZB != ZB_Undefined && Val == 0)
+ return 64;
+
+#if defined(_MSC_VER)
+ unsigned long Index;
+ _BitScanReverse64(&Index, Val);
+ return Index ^ 63;
+#else
+ return __builtin_clzll(Val);
+#endif
+ }
+};
+#endif
+#endif
+
+/// \brief Count number of 0's from the most significant bit to the least
+/// stopping at the first 1.
+///
+/// Only unsigned integral types are allowed.
+///
+/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
+/// valid arguments.
+template
+std::size_t countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
+ static_assert(std::numeric_limits::is_integer &&
+ !std::numeric_limits::is_signed,
+ "Only unsigned integral types are allowed.");
+ return LeadingZerosCounter::count(Val, ZB);
+}
+
+/// Make a 64-bit integer from a high / low pair of 32-bit integers.
+constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) {
+ return ((uint64_t)High << 32) | (uint64_t)Low;
+}
+
+/// Return the high 32 bits of a 64 bit value.
+constexpr inline uint32_t Hi_32(uint64_t Value) {
+ return static_cast(Value >> 32);
+}
+
+/// Return the low 32 bits of a 64 bit value.
+constexpr inline uint32_t Lo_32(uint64_t Value) {
+ return static_cast(Value);
+}
+
+/// Implementation of Knuth's Algorithm D (Division of nonnegative integers)
+/// from "Art of Computer Programming, Volume 2", section 4.3.1, p. 272. The
+/// variables here have the same names as in the algorithm. Comments explain
+/// the algorithm and any deviation from it.
+static void KnuthDiv(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t* r,
+ unsigned m, unsigned n)
+{
+ assert(u && "Must provide dividend");
+ assert(v && "Must provide divisor");
+ assert(q && "Must provide quotient");
+ assert(u != v && u != q && v != q && "Must use different memory");
+ assert(n>1 && "n must be > 1");
+
+ // b denotes the base of the number system. In our case b is 2^32.
+ const uint64_t b = uint64_t(1) << 32;
+
+ // D1. [Normalize.] Set d = b / (v[n-1] + 1) and multiply all the digits of
+ // u and v by d. Note that we have taken Knuth's advice here to use a power
+ // of 2 value for d such that d * v[n-1] >= b/2 (b is the base). A power of
+ // 2 allows us to shift instead of multiply and it is easy to determine the
+ // shift amount from the leading zeros. We are basically normalizing the u
+ // and v so that its high bits are shifted to the top of v's range without
+ // overflow. Note that this can require an extra word in u so that u must
+ // be of length m+n+1.
+ unsigned shift = countLeadingZeros(v[n-1]);
+ uint32_t v_carry = 0;
+ uint32_t u_carry = 0;
+ if (shift) {
+ for (unsigned i = 0; i < m+n; ++i) {
+ uint32_t u_tmp = u[i] >> (32 - shift);
+ u[i] = (u[i] << shift) | u_carry;
+ u_carry = u_tmp;
+ }
+ for (unsigned i = 0; i < n; ++i) {
+ uint32_t v_tmp = v[i] >> (32 - shift);
+ v[i] = (v[i] << shift) | v_carry;
+ v_carry = v_tmp;
+ }
+ }
+ u[m+n] = u_carry;
+
+ // D2. [Initialize j.] Set j to m. This is the loop counter over the places.
+ int j = m;
+ do {
+ // D3. [Calculate q'.].
+ // Set qp = (u[j+n]*b + u[j+n-1]) / v[n-1]. (qp=qprime=q')
+ // Set rp = (u[j+n]*b + u[j+n-1]) % v[n-1]. (rp=rprime=r')
+ // Now test if qp == b or qp*v[n-2] > b*rp + u[j+n-2]; if so, decrease
+ // qp by 1, increase rp by v[n-1], and repeat this test if rp < b. The test
+ // on v[n-2] determines at high speed most of the cases in which the trial
+ // value qp is one too large, and it eliminates all cases where qp is two
+ // too large.
+ uint64_t dividend = Make_64(u[j+n], u[j+n-1]);
+ uint64_t qp = dividend / v[n-1];
+ uint64_t rp = dividend % v[n-1];
+ if (qp == b || qp*v[n-2] > b*rp + u[j+n-2]) {
+ qp--;
+ rp += v[n-1];
+ if (rp < b && (qp == b || qp*v[n-2] > b*rp + u[j+n-2]))
+ qp--;
+ }
+
+ // D4. [Multiply and subtract.] Replace (u[j+n]u[j+n-1]...u[j]) with
+ // (u[j+n]u[j+n-1]..u[j]) - qp * (v[n-1]...v[1]v[0]). This computation
+ // consists of a simple multiplication by a one-place number, combined with
+ // a subtraction.
+ // The digits (u[j+n]...u[j]) should be kept positive; if the result of
+ // this step is actually negative, (u[j+n]...u[j]) should be left as the
+ // true value plus b**(n+1), namely as the b's complement of
+ // the true value, and a "borrow" to the left should be remembered.
+ int64_t borrow = 0;
+ for (unsigned i = 0; i < n; ++i) {
+ uint64_t p = uint64_t(qp) * uint64_t(v[i]);
+ int64_t subres = int64_t(u[j+i]) - borrow - Lo_32(p);
+ u[j+i] = Lo_32(subres);
+ borrow = Hi_32(p) - Hi_32(subres);
+ }
+ bool isNeg = u[j+n] < borrow;
+ u[j+n] -= Lo_32(borrow);
+
+ // D5. [Test remainder.] Set q[j] = qp. If the result of step D4 was
+ // negative, go to step D6; otherwise go on to step D7.
+ q[j] = Lo_32(qp);
+ if (isNeg) {
+ // D6. [Add back]. The probability that this step is necessary is very
+ // small, on the order of only 2/b. Make sure that test data accounts for
+ // this possibility. Decrease q[j] by 1
+ q[j]--;
+ // and add (0v[n-1]...v[1]v[0]) to (u[j+n]u[j+n-1]...u[j+1]u[j]).
+ // A carry will occur to the left of u[j+n], and it should be ignored
+ // since it cancels with the borrow that occurred in D4.
+ bool carry = false;
+ for (unsigned i = 0; i < n; i++) {
+ uint32_t limit = std::min(u[j+i],v[i]);
+ u[j+i] += v[i] + carry;
+ carry = u[j+i] < limit || (carry && u[j+i] == limit);
+ }
+ u[j+n] += carry;
+ }
+
+ // D7. [Loop on j.] Decrease j by one. Now if j >= 0, go back to D3.
+ } while (--j >= 0);
+
+ // D8. [Unnormalize]. Now q[...] is the desired quotient, and the desired
+ // remainder may be obtained by dividing u[...] by d. If r is non-null we
+ // compute the remainder (urem uses this).
+ if (r) {
+ // The value d is expressed by the "shift" value above since we avoided
+ // multiplication by d by using a shift left. So, all we have to do is
+ // shift right here.
+ if (shift) {
+ uint32_t carry = 0;
+ for (int i = n-1; i >= 0; i--) {
+ r[i] = (u[i] >> shift) | carry;
+ carry = u[i] << (32 - shift);
+ }
+ } else {
+ for (int i = n-1; i >= 0; i--) {
+ r[i] = u[i];
+ }
+ }
+ }
+}
+
+// Implementation ported from LLVM/lib/Support/APInt.cpp
+static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigInt *Quotient, BigInt *Remainder) {
+ Cmp cmp = bigint_cmp(op1, op2);
+ if (cmp == CmpLT) {
+ if (Quotient != nullptr) {
+ bigint_init_unsigned(Quotient, 0);
+ }
+ if (Remainder != nullptr) {
+ bigint_init_bigint(Remainder, op1);
+ }
+ return;
+ }
+ if (cmp == CmpEQ) {
+ if (Quotient != nullptr) {
+ bigint_init_unsigned(Quotient, 1);
+ }
+ if (Remainder != nullptr) {
+ bigint_init_unsigned(Remainder, 0);
+ }
+ return;
+ }
+
+ const uint64_t *LHS = bigint_ptr(op1);
+ const uint64_t *RHS = bigint_ptr(op2);
+ unsigned lhsWords = op1->digit_count;
+ unsigned rhsWords = op2->digit_count;
+
+ // First, compose the values into an array of 32-bit words instead of
+ // 64-bit words. This is a necessity of both the "short division" algorithm
+ // and the Knuth "classical algorithm" which requires there to be native
+ // operations for +, -, and * on an m bit value with an m*2 bit result. We
+ // can't use 64-bit operands here because we don't have native results of
+ // 128-bits. Furthermore, casting the 64-bit values to 32-bit values won't
+ // work on large-endian machines.
+ unsigned n = rhsWords * 2;
+ unsigned m = (lhsWords * 2) - n;
+
+ // Allocate space for the temporary values we need either on the stack, if
+ // it will fit, or on the heap if it won't.
+ uint32_t SPACE[128];
+ uint32_t *U = nullptr;
+ uint32_t *V = nullptr;
+ uint32_t *Q = nullptr;
+ uint32_t *R = nullptr;
+ if ((Remainder?4:3)*n+2*m+1 <= 128) {
+ U = &SPACE[0];
+ V = &SPACE[m+n+1];
+ Q = &SPACE[(m+n+1) + n];
+ if (Remainder)
+ R = &SPACE[(m+n+1) + n + (m+n)];
+ } else {
+ U = new uint32_t[m + n + 1];
+ V = new uint32_t[n];
+ Q = new uint32_t[m+n];
+ if (Remainder)
+ R = new uint32_t[n];
+ }
+
+ // Initialize the dividend
+ memset(U, 0, (m+n+1)*sizeof(uint32_t));
+ for (unsigned i = 0; i < lhsWords; ++i) {
+ uint64_t tmp = LHS[i];
+ U[i * 2] = Lo_32(tmp);
+ U[i * 2 + 1] = Hi_32(tmp);
+ }
+ U[m+n] = 0; // this extra word is for "spill" in the Knuth algorithm.
+
+ // Initialize the divisor
+ memset(V, 0, (n)*sizeof(uint32_t));
+ for (unsigned i = 0; i < rhsWords; ++i) {
+ uint64_t tmp = RHS[i];
+ V[i * 2] = Lo_32(tmp);
+ V[i * 2 + 1] = Hi_32(tmp);
+ }
+
+ // initialize the quotient and remainder
+ memset(Q, 0, (m+n) * sizeof(uint32_t));
+ if (Remainder)
+ memset(R, 0, n * sizeof(uint32_t));
+
+ // Now, adjust m and n for the Knuth division. n is the number of words in
+ // the divisor. m is the number of words by which the dividend exceeds the
+ // divisor (i.e. m+n is the length of the dividend). These sizes must not
+ // contain any zero words or the Knuth algorithm fails.
+ for (unsigned i = n; i > 0 && V[i-1] == 0; i--) {
+ n--;
+ m++;
+ }
+ for (unsigned i = m+n; i > 0 && U[i-1] == 0; i--)
+ m--;
+
+ // If we're left with only a single word for the divisor, Knuth doesn't work
+ // so we implement the short division algorithm here. This is much simpler
+ // and faster because we are certain that we can divide a 64-bit quantity
+ // by a 32-bit quantity at hardware speed and short division is simply a
+ // series of such operations. This is just like doing short division but we
+ // are using base 2^32 instead of base 10.
+ assert(n != 0 && "Divide by zero?");
+ if (n == 1) {
+ uint32_t divisor = V[0];
+ uint32_t remainder = 0;
+ for (int i = m; i >= 0; i--) {
+ uint64_t partial_dividend = Make_64(remainder, U[i]);
+ if (partial_dividend == 0) {
+ Q[i] = 0;
+ remainder = 0;
+ } else if (partial_dividend < divisor) {
+ Q[i] = 0;
+ remainder = Lo_32(partial_dividend);
+ } else if (partial_dividend == divisor) {
+ Q[i] = 1;
+ remainder = 0;
+ } else {
+ Q[i] = Lo_32(partial_dividend / divisor);
+ remainder = Lo_32(partial_dividend - (Q[i] * divisor));
+ }
+ }
+ if (R)
+ R[0] = remainder;
+ } else {
+ // Now we're ready to invoke the Knuth classical divide algorithm. In this
+ // case n > 1.
+ KnuthDiv(U, V, Q, R, m, n);
+ }
+
+ // If the caller wants the quotient
+ if (Quotient) {
+ Quotient->digit_count = lhsWords;
+ Quotient->data.digits = allocate(lhsWords);
+ Quotient->is_negative = false;
+ for (size_t i = 0; i < lhsWords; i += 1) {
+ Quotient->data.digits[i] = Make_64(Q[i*2+1], Q[i*2]);
+ }
+ }
+
+ // If the caller wants the remainder
+ if (Remainder) {
+ Remainder->digit_count = rhsWords;
+ Remainder->data.digits = allocate(rhsWords);
+ Remainder->is_negative = false;
+ for (size_t i = 0; i < rhsWords; i += 1) {
+ Remainder->data.digits[i] = Make_64(R[i*2+1], R[i*2]);
+ }
+ }
+}
+
void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2) {
assert(op2->digit_count != 0); // division by zero
if (op1->digit_count == 0) {
bigint_init_unsigned(dest, 0);
return;
}
- if (op1->digit_count != 1 || op2->digit_count != 1) {
- zig_panic("TODO bigint div_trunc with >1 digits");
- }
const uint64_t *op1_digits = bigint_ptr(op1);
const uint64_t *op2_digits = bigint_ptr(op2);
- dest->data.digit = op1_digits[0] / op2_digits[0];
- dest->digit_count = 1;
+ if (op1->digit_count == 1 && op2->digit_count == 1) {
+ dest->data.digit = op1_digits[0] / op2_digits[0];
+ dest->digit_count = 1;
+ dest->is_negative = op1->is_negative != op2->is_negative;
+ bigint_normalize(dest);
+ return;
+ }
+ if (op2->digit_count == 1 && op2_digits[0] == 1) {
+ // X / 1 == X
+ bigint_init_bigint(dest, op1);
+ dest->is_negative = op1->is_negative != op2->is_negative;
+ bigint_normalize(dest);
+ return;
+ }
+
+ const BigInt *op1_positive;
+ BigInt op1_positive_data;
+ if (op1->is_negative) {
+ bigint_negate(&op1_positive_data, op1);
+ op1_positive = &op1_positive_data;
+ } else {
+ op1_positive = op1;
+ }
+
+ const BigInt *op2_positive;
+ BigInt op2_positive_data;
+ if (op2->is_negative) {
+ bigint_negate(&op2_positive_data, op2);
+ op2_positive = &op2_positive_data;
+ } else {
+ op2_positive = op2;
+ }
+
+ bigint_unsigned_division(op1_positive, op2_positive, dest, nullptr);
dest->is_negative = op1->is_negative != op2->is_negative;
bigint_normalize(dest);
}
@@ -714,6 +1107,14 @@ void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) {
}
const uint64_t *op1_digits = bigint_ptr(op1);
const uint64_t *op2_digits = bigint_ptr(op2);
+
+ if (op1->digit_count == 1 && op2->digit_count == 1) {
+ dest->data.digit = op1_digits[0] % op2_digits[0];
+ dest->digit_count = 1;
+ dest->is_negative = op1->is_negative;
+ bigint_normalize(dest);
+ return;
+ }
if (op2->digit_count == 2 && op2_digits[0] == 0 && op2_digits[1] == 1) {
// special case this divisor
bigint_init_unsigned(dest, op1_digits[0]);
@@ -721,11 +1122,32 @@ void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) {
bigint_normalize(dest);
return;
}
- if (op1->digit_count != 1 || op2->digit_count != 1) {
- zig_panic("TODO bigint rem with >1 digits");
+
+ if (op2->digit_count == 1 && op2_digits[0] == 1) {
+ // X % 1 == 0
+ bigint_init_unsigned(dest, 0);
+ return;
}
- dest->data.digit = op1_digits[0] % op2_digits[0];
- dest->digit_count = 1;
+
+ const BigInt *op1_positive;
+ BigInt op1_positive_data;
+ if (op1->is_negative) {
+ bigint_negate(&op1_positive_data, op1);
+ op1_positive = &op1_positive_data;
+ } else {
+ op1_positive = op1;
+ }
+
+ const BigInt *op2_positive;
+ BigInt op2_positive_data;
+ if (op2->is_negative) {
+ bigint_negate(&op2_positive_data, op2);
+ op2_positive = &op2_positive_data;
+ } else {
+ op2_positive = op2;
+ }
+
+ bigint_unsigned_division(op1_positive, op2_positive, nullptr, dest);
dest->is_negative = op1->is_negative;
bigint_normalize(dest);
}
diff --git a/test/cases/math.zig b/test/cases/math.zig
index b4e0e4cfd6d0b77b9dc459f94b1af5d5f80300d8..9b949ffd166b72470eb63c240a530c3f4c16b4b5 100644
--- a/test/cases/math.zig
+++ b/test/cases/math.zig
@@ -26,6 +26,28 @@ fn testDivision() {
assert(divTrunc(i32, -5, 3) == -1);
assert(divTrunc(f32, 5.0, 3.0) == 1.0);
assert(divTrunc(f32, -5.0, 3.0) == -1.0);
+
+ comptime {
+ assert(
+ 1194735857077236777412821811143690633098347576 %
+ 508740759824825164163191790951174292733114988 ==
+ 177254337427586449086438229241342047632117600);
+ assert(@rem(-1194735857077236777412821811143690633098347576,
+ 508740759824825164163191790951174292733114988) ==
+ -177254337427586449086438229241342047632117600);
+ assert(1194735857077236777412821811143690633098347576 /
+ 508740759824825164163191790951174292733114988 ==
+ 2);
+ assert(@divTrunc(-1194735857077236777412821811143690633098347576,
+ 508740759824825164163191790951174292733114988) ==
+ -2);
+ assert(@divTrunc(1194735857077236777412821811143690633098347576,
+ -508740759824825164163191790951174292733114988) ==
+ -2);
+ assert(@divTrunc(-1194735857077236777412821811143690633098347576,
+ -508740759824825164163191790951174292733114988) ==
+ 2);
+ }
}
fn div(comptime T: type, a: T, b: T) -> T {
return a / b;
--
2.54.0
From 6a95b88d1b3a619532feedffc674fc4b5bf7517b Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 16 Jan 2018 03:09:44 -0500
Subject: [PATCH 20/25] fix bigint remainder division
See #405
---
src/bigint.cpp | 24 ++++++++++++++++--------
test/cases/math.zig | 1 +
2 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/src/bigint.cpp b/src/bigint.cpp
index 3e47a11900346c5ae0a3e74f7c251e135fce6115..a68dd3a4b830c3e727d7c594a5e5f389c1f66804 100644
--- a/src/bigint.cpp
+++ b/src/bigint.cpp
@@ -1015,21 +1015,29 @@ static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigIn
// If the caller wants the quotient
if (Quotient) {
- Quotient->digit_count = lhsWords;
- Quotient->data.digits = allocate(lhsWords);
Quotient->is_negative = false;
- for (size_t i = 0; i < lhsWords; i += 1) {
- Quotient->data.digits[i] = Make_64(Q[i*2+1], Q[i*2]);
+ Quotient->digit_count = lhsWords;
+ if (lhsWords == 1) {
+ Quotient->data.digit = Make_64(Q[1], Q[0]);
+ } else {
+ Quotient->data.digits = allocate(lhsWords);
+ for (size_t i = 0; i < lhsWords; i += 1) {
+ Quotient->data.digits[i] = Make_64(Q[i*2+1], Q[i*2]);
+ }
}
}
// If the caller wants the remainder
if (Remainder) {
- Remainder->digit_count = rhsWords;
- Remainder->data.digits = allocate(rhsWords);
Remainder->is_negative = false;
- for (size_t i = 0; i < rhsWords; i += 1) {
- Remainder->data.digits[i] = Make_64(R[i*2+1], R[i*2]);
+ Remainder->digit_count = rhsWords;
+ if (rhsWords == 1) {
+ Remainder->data.digit = Make_64(R[1], R[0]);
+ } else {
+ Remainder->data.digits = allocate(rhsWords);
+ for (size_t i = 0; i < rhsWords; i += 1) {
+ Remainder->data.digits[i] = Make_64(R[i*2+1], R[i*2]);
+ }
}
}
}
diff --git a/test/cases/math.zig b/test/cases/math.zig
index 9b949ffd166b72470eb63c240a530c3f4c16b4b5..090e2b9dfdf810b223ec4b41a73c3c10550b686a 100644
--- a/test/cases/math.zig
+++ b/test/cases/math.zig
@@ -47,6 +47,7 @@ fn testDivision() {
assert(@divTrunc(-1194735857077236777412821811143690633098347576,
-508740759824825164163191790951174292733114988) ==
2);
+ assert(4126227191251978491697987544882340798050766755606969681711 % 10 == 1);
}
}
fn div(comptime T: type, a: T, b: T) -> T {
--
2.54.0
From fa7b33549e43abbdb4fba1d686dfd63c2ca8e8ca Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Tue, 16 Jan 2018 21:35:31 +1300
Subject: [PATCH 21/25] Change crypto functions to fill a buffer
- Rename blake2x -> blake2
- Fix blake2s truncated tests
---
CMakeLists.txt | 2 +-
std/crypto/{blake2x.zig => blake2.zig} | 230 +++++++++++++------------
std/crypto/index.zig | 12 +-
std/crypto/md5.zig | 47 ++---
std/crypto/sha1.zig | 38 ++--
std/crypto/sha2.zig | 121 +++++++------
6 files changed, 238 insertions(+), 212 deletions(-)
rename std/crypto/{blake2x.zig => blake2.zig} (65%)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b93951e4e620bfb32abdf12e1c2c54e20507857d..8a090d9f29189f2352ee2da4ad6bad16d614df28 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -368,7 +368,7 @@ set(ZIG_STD_FILES
"crypto/md5.zig"
"crypto/sha1.zig"
"crypto/sha2.zig"
- "crypto/blake2x.zig"
+ "crypto/blake2.zig"
"cstr.zig"
"debug/failing_allocator.zig"
"debug/index.zig"
diff --git a/std/crypto/blake2x.zig b/std/crypto/blake2.zig
similarity index 65%
rename from std/crypto/blake2x.zig
rename to std/crypto/blake2.zig
index 67ef00610b2370501e971958910aa4da79c860d7..b08caa480eb4e535068d19337d73097a75d9a063 100644
--- a/std/crypto/blake2x.zig
+++ b/std/crypto/blake2.zig
@@ -1,7 +1,9 @@
-const std = @import("std");
-const mem = std.mem;
-const math = std.math;
-const debug = std.debug;
+const mem = @import("../mem.zig");
+const math = @import("../math/index.zig");
+const endian = @import("../endian.zig");
+const debug = @import("../debug/index.zig");
+const builtin = @import("builtin");
+const htest = @import("test.zig");
const RoundParam = struct {
a: usize, b: usize, c: usize, d: usize, x: usize, y: usize,
@@ -19,7 +21,6 @@ pub const Blake2s256 = Blake2s(256);
fn Blake2s(comptime out_len: usize) -> type { return struct {
const Self = this;
- const ReturnType = @IntType(false, out_len);
const iv = [8]u32 {
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
@@ -62,10 +63,10 @@ fn Blake2s(comptime out_len: usize) -> type { return struct {
d.buf_len = 0;
}
- pub fn hash(b: []const u8) -> ReturnType {
+ pub fn hash(b: []const u8, out: []u8) {
var d = Self.init();
d.update(b);
- return d.final();
+ d.final(out);
}
pub fn update(d: &Self, b: []const u8) {
@@ -91,22 +92,18 @@ fn Blake2s(comptime out_len: usize) -> type { return struct {
d.buf_len += u8(b[off..].len);
}
- pub fn final(d: &Self) -> ReturnType {
+ pub fn final(d: &Self, out: []u8) {
+ debug.assert(out.len >= out_len / 8);
+
mem.set(u8, d.buf[d.buf_len..], 0);
d.t += d.buf_len;
d.round(d.buf[0..], true);
const rr = d.h[0 .. out_len / 32];
- // NOTE: mem.readIntLE or equivalent would be useful here.
- var j: u8 = 0;
- var r: ReturnType = 0;
- for (rr) |p| {
- r |= ReturnType(p) << j;
- j +%= 32;
+ for (rr) |s, j| {
+ mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Little);
}
-
- return std.endian.swapIfLe(ReturnType, r);
}
fn round(d: &Self, b: []const u8, last: bool) {
@@ -160,66 +157,74 @@ fn Blake2s(comptime out_len: usize) -> type { return struct {
}
};}
-// TODO: bigint rem >1 digits for 224 integer output.
-//
-// test "blake2s224 single" {
-// const hash1 = 0xa847d26c2f966c5c4cc222b174918a56037cdee34b3f872f;
-// debug.assert(hash1 == Blake2s224.hash(""));
-//
-// const hash2 = 0x1e2ed10fcdbc46e0ab3ea3f268a6c288083ae04e3d63a8de;
-// debug.assert(hash2 == Blake2s224.hash("abc"));
-//
-// const hash3 = 0xe486adf7b22d2944b434ae78ae64720c16ccf0479dab072d;
-// debug.assert(hash3 == Blake2s224.hash("The quick brown fox jumps over the lazy dog"));
-// }
-//
-// test "blake2s224 streaming" {
-// var h = Blake2s224.init();
-//
-// const hash1 = 0xa847d26c2f966c5c4cc222b174918a56037cdee34b3f872f;
-// debug.assert(hash1 == h.final());
-//
-// const hash2 = 0x1e2ed10fcdbc46e0ab3ea3f268a6c288083ae04e3d63a8de;
-//
-// h.reset();
-// h.update("abc");
-// debug.assert(hash2 == h.final());
-//
-// h.reset();
-// h.update("a");
-// h.update("b");
-// h.update("c");
-// debug.assert(hash2 == h.final());
-// }
+test "blake2s224 single" {
+ const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4";
+ htest.assertEqualHash(Blake2s224, h1, "");
+
+ const h2 = "0b033fc226df7abde29f67a05d3dc62cf271ef3dfea4d387407fbd55";
+ htest.assertEqualHash(Blake2s224, h2, "abc");
+
+ const h3 = "e4e5cb6c7cae41982b397bf7b7d2d9d1949823ae78435326e8db4912";
+ htest.assertEqualHash(Blake2s224, h3, "The quick brown fox jumps over the lazy dog");
+}
+
+test "blake2s224 streaming" {
+ var h = Blake2s224.init();
+ var out: [28]u8 = undefined;
+
+ const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4";
+
+ h.final(out[0..]);
+ htest.assertEqual(h1, out[0..]);
+
+ const h2 = "0b033fc226df7abde29f67a05d3dc62cf271ef3dfea4d387407fbd55";
+
+ h.reset();
+ h.update("abc");
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
+
+ h.reset();
+ h.update("a");
+ h.update("b");
+ h.update("c");
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
+}
test "blake2s256 single" {
- const hash1 = 0x69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9;
- debug.assert(hash1 == Blake2s256.hash(""));
+ const h1 = "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9";
+ htest.assertEqualHash(Blake2s256, h1, "");
- const hash2 = 0x508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982;
- debug.assert(hash2 == Blake2s256.hash("abc"));
+ const h2 = "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982";
+ htest.assertEqualHash(Blake2s256, h2, "abc");
- const hash3 = 0x606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812;
- debug.assert(hash3 == Blake2s256.hash("The quick brown fox jumps over the lazy dog"));
+ const h3 = "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812";
+ htest.assertEqualHash(Blake2s256, h3, "The quick brown fox jumps over the lazy dog");
}
test "blake2s256 streaming" {
var h = Blake2s256.init();
+ var out: [32]u8 = undefined;
- const hash1 = 0x69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9;
- debug.assert(hash1 == h.final());
+ const h1 = "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9";
- const hash2 = 0x508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982;
+ h.final(out[0..]);
+ htest.assertEqual(h1, out[0..]);
+
+ const h2 = "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982";
h.reset();
h.update("abc");
- debug.assert(hash2 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
h.reset();
h.update("a");
h.update("b");
h.update("c");
- debug.assert(hash2 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
}
@@ -231,8 +236,6 @@ pub const Blake2b512 = Blake2b(512);
fn Blake2b(comptime out_len: usize) -> type { return struct {
const Self = this;
- const ReturnType = @IntType(false, out_len);
- const u9 = @IntType(false, 9);
const iv = [8]u64 {
0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
@@ -279,10 +282,10 @@ fn Blake2b(comptime out_len: usize) -> type { return struct {
d.buf_len = 0;
}
- pub fn hash(b: []const u8) -> ReturnType {
+ pub fn hash(b: []const u8, out: []u8) {
var d = Self.init();
d.update(b);
- return d.final();
+ d.final(out);
}
pub fn update(d: &Self, b: []const u8) {
@@ -308,21 +311,16 @@ fn Blake2b(comptime out_len: usize) -> type { return struct {
d.buf_len += u8(b[off..].len);
}
- pub fn final(d: &Self) -> ReturnType {
+ pub fn final(d: &Self, out: []u8) {
mem.set(u8, d.buf[d.buf_len..], 0);
d.t += d.buf_len;
d.round(d.buf[0..], true);
const rr = d.h[0 .. out_len / 64];
- var j: u9 = 0;
- var r: ReturnType = 0;
- for (rr) |p| {
- r |= ReturnType(p) << j;
- j +%= 64;
+ for (rr) |s, j| {
+ mem.writeInt(out[8*j .. 8*j + 8], s, builtin.Endian.Little);
}
-
- return std.endian.swapIfLe(ReturnType, r);
}
fn round(d: &Self, b: []const u8, last: bool) {
@@ -376,64 +374,72 @@ fn Blake2b(comptime out_len: usize) -> type { return struct {
}
};}
-// TODO: bigint rem >1 digits for 384 integer output.
-
-// test "blake2b384 single" {
-// const hash1 = 0xb32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100;
-// debug.assert(hash1 == Blake2b384.hash(""));
-//
-// const hash2 = 0x6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4;
-// debug.assert(hash2 == Blake2b384.hash("abc"));
-//
-// const hash3 = 0xb7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d;
-// debug.assert(hash3 == Blake2b384.hash("The quick brown fox jumps over the lazy dog"));
-// }
-//
-// test "blake2b384 streaming" {
-// var h = Blake2b384.init();
-//
-// const hash1 = 0xb32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100;
-// debug.assert(hash1 == h.final());
-//
-// const hash2 = 0x6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4;
-//
-// h.reset();
-// h.update("abc");
-// debug.assert(hash2 == h.final());
-//
-// h.reset();
-// h.update("a");
-// h.update("b");
-// h.update("c");
-// debug.assert(hash2 == h.final());
-// }
+test "blake2b384 single" {
+ const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100";
+ htest.assertEqualHash(Blake2b384, h1, "");
+
+ const h2 = "6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4";
+ htest.assertEqualHash(Blake2b384, h2, "abc");
+
+ const h3 = "b7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d";
+ htest.assertEqualHash(Blake2b384, h3, "The quick brown fox jumps over the lazy dog");
+}
+
+test "blake2b384 streaming" {
+ var h = Blake2b384.init();
+ var out: [48]u8 = undefined;
+
+ const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100";
+
+ h.final(out[0..]);
+ htest.assertEqual(h1, out[0..]);
+
+ const h2 = "6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4";
+
+ h.reset();
+ h.update("abc");
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
+
+ h.reset();
+ h.update("a");
+ h.update("b");
+ h.update("c");
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
+}
test "blake2b512 single" {
- const hash1 = 0x786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce;
- debug.assert(hash1 == Blake2b512.hash(""));
+ const h1 = "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce";
+ htest.assertEqualHash(Blake2b512, h1, "");
- const hash2 = 0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923;
- debug.assert(hash2 == Blake2b512.hash("abc"));
+ const h2 = "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923";
+ htest.assertEqualHash(Blake2b512, h2, "abc");
- const hash3 = 0xa8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918;
- debug.assert(hash3 == Blake2b512.hash("The quick brown fox jumps over the lazy dog"));
+ const h3 = "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918";
+ htest.assertEqualHash(Blake2b512, h3, "The quick brown fox jumps over the lazy dog");
}
test "blake2b512 streaming" {
var h = Blake2b512.init();
+ var out: [64]u8 = undefined;
- const hash1 = 0x786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce;
- debug.assert(hash1 == h.final());
+ const h1 = "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce";
- const hash2 = 0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923;
+ h.final(out[0..]);
+ htest.assertEqual(h1, out[0..]);
+
+ const h2 = "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923";
h.reset();
h.update("abc");
- debug.assert(hash2 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
h.reset();
h.update("a");
h.update("b");
h.update("c");
- debug.assert(hash2 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
}
diff --git a/std/crypto/index.zig b/std/crypto/index.zig
index a63984e3d5dc286ff7a1be18c46e94fe5bb40f04..839704e3e7035afd57bf9d72513d9b107457a0ba 100644
--- a/std/crypto/index.zig
+++ b/std/crypto/index.zig
@@ -7,15 +7,15 @@ pub const Sha256 = sha2.Sha256;
pub const Sha384 = sha2.Sha384;
pub const Sha512 = sha2.Sha512;
-const blake2x = @import("blake2x.zig");
-pub const Blake2s224 = blake2x.Blake2s224;
-pub const Blake2s256 = blake2x.Blake2s256;
-pub const Blake2b384 = blake2x.Blake2b384;
-pub const Blake2b512 = blake2x.Blake2b512;
+const blake2 = @import("blake2.zig");
+pub const Blake2s224 = blake2.Blake2s224;
+pub const Blake2s256 = blake2.Blake2s256;
+pub const Blake2b384 = blake2.Blake2b384;
+pub const Blake2b512 = blake2.Blake2b512;
test "crypto" {
_ = @import("md5.zig");
_ = @import("sha1.zig");
_ = @import("sha2.zig");
- _ = @import("blake2x.zig");
+ _ = @import("blake2.zig");
}
diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig
index c01f288c56eb82bbf0b787085bbc3d51d69956cb..02f195438083b0dd4bbddb9f0ee46e500b6530f9 100644
--- a/std/crypto/md5.zig
+++ b/std/crypto/md5.zig
@@ -1,7 +1,9 @@
const mem = @import("../mem.zig");
const math = @import("../math/index.zig");
const endian = @import("../endian.zig");
+const builtin = @import("builtin");
const debug = @import("../debug/index.zig");
+const fmt = @import("../fmt/index.zig");
const RoundParam = struct {
a: usize, b: usize, c: usize, d: usize,
@@ -42,10 +44,10 @@ pub const Md5 = struct {
d.total_len = 0;
}
- pub fn hash(b: []const u8) -> u128 {
+ pub fn hash(b: []const u8, out: []u8) {
var d = Md5.init();
d.update(b);
- return d.final();
+ d.final(out);
}
pub fn update(d: &Self, b: []const u8) {
@@ -73,7 +75,9 @@ pub const Md5 = struct {
d.total_len +%= b.len;
}
- pub fn final(d: &Self) -> u128 {
+ pub fn final(d: &Self, out: []u8) {
+ debug.assert(out.len >= 16);
+
// The buffer here will never be completely full.
mem.set(u8, d.buf[d.buf_len..], 0);
@@ -98,13 +102,9 @@ pub const Md5 = struct {
d.round(d.buf[0..]);
- const r =
- (u128(d.s[3]) << 96) |
- (u128(d.s[2]) << 64) |
- (u128(d.s[1]) << 32) |
- (u128(d.s[0]) << 0);
-
- return endian.swapIfLe(u128, r);
+ for (d.s) |s, j| {
+ mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Little);
+ }
}
fn round(d: &Self, b: []const u8) {
@@ -226,28 +226,35 @@ pub const Md5 = struct {
}
};
+const htest = @import("test.zig");
+
test "md5 single" {
- debug.assert(0xd41d8cd98f00b204e9800998ecf8427e == Md5.hash(""));
- debug.assert(0x0cc175b9c0f1b6a831c399e269772661 == Md5.hash("a"));
- debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == Md5.hash("abc"));
- debug.assert(0xf96b697d7cb7938d525a2f31aaf161d0 == Md5.hash("message digest"));
- debug.assert(0xc3fcd3d76192e4007dfb496cca67e13b == Md5.hash("abcdefghijklmnopqrstuvwxyz"));
- debug.assert(0xd174ab98d277d9f5a5611c2c9f419d9f == Md5.hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"));
- debug.assert(0x57edf4a22be3c955ac49da2e2107b67a == Md5.hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890"));
+ htest.assertEqualHash(Md5, "d41d8cd98f00b204e9800998ecf8427e", "");
+ htest.assertEqualHash(Md5, "0cc175b9c0f1b6a831c399e269772661", "a");
+ htest.assertEqualHash(Md5, "900150983cd24fb0d6963f7d28e17f72", "abc");
+ htest.assertEqualHash(Md5, "f96b697d7cb7938d525a2f31aaf161d0", "message digest");
+ htest.assertEqualHash(Md5, "c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz");
+ htest.assertEqualHash(Md5, "d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ htest.assertEqualHash(Md5, "57edf4a22be3c955ac49da2e2107b67a", "12345678901234567890123456789012345678901234567890123456789012345678901234567890");
}
test "md5 streaming" {
var h = Md5.init();
+ var out: [16]u8 = undefined;
- debug.assert(0xd41d8cd98f00b204e9800998ecf8427e == h.final());
+ h.final(out[0..]);
+ htest.assertEqual("d41d8cd98f00b204e9800998ecf8427e", out[0..]);
h.reset();
h.update("abc");
- debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual("900150983cd24fb0d6963f7d28e17f72", out[0..]);
h.reset();
h.update("a");
h.update("b");
h.update("c");
- debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == h.final());
+ h.final(out[0..]);
+
+ htest.assertEqual("900150983cd24fb0d6963f7d28e17f72", out[0..]);
}
diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig
index 5c8278700c78ac75345aac3f0cd8b7c91e803686..bb68b62983f6b8f35a5d0b812f2529fcd1fdc1b2 100644
--- a/std/crypto/sha1.zig
+++ b/std/crypto/sha1.zig
@@ -2,6 +2,7 @@ const mem = @import("../mem.zig");
const math = @import("../math/index.zig");
const endian = @import("../endian.zig");
const debug = @import("../debug/index.zig");
+const builtin = @import("builtin");
pub const u160 = @IntType(false, 160);
@@ -38,10 +39,10 @@ pub const Sha1 = struct {
d.total_len = 0;
}
- pub fn hash(b: []const u8) -> u160 {
+ pub fn hash(b: []const u8, out: []u8) {
var d = Sha1.init();
d.update(b);
- return d.final();
+ d.final(out);
}
pub fn update(d: &Self, b: []const u8) {
@@ -68,7 +69,9 @@ pub const Sha1 = struct {
d.total_len += b.len;
}
- pub fn final(d: &Self) -> u160 {
+ pub fn final(d: &Self, out: []u8) {
+ debug.assert(out.len >= 20);
+
// The buffer here will never be completely full.
mem.set(u8, d.buf[d.buf_len..], 0);
@@ -93,14 +96,9 @@ pub const Sha1 = struct {
d.round(d.buf[0..]);
- const r =
- (u160(d.s[0]) << 128) |
- (u160(d.s[1]) << 96) |
- (u160(d.s[2]) << 64) |
- (u160(d.s[3]) << 32) |
- (u160(d.s[4]) << 0);
-
- return endian.swapIfBe(u160, r);
+ for (d.s) |s, j| {
+ mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Big);
+ }
}
fn round(d: &Self, b: []const u8) {
@@ -257,24 +255,30 @@ pub const Sha1 = struct {
}
};
+const htest = @import("test.zig");
+
test "sha1 single" {
- debug.assert(0xda39a3ee5e6b4b0d3255bfef95601890afd80709 == Sha1.hash(""));
- debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == Sha1.hash("abc"));
- debug.assert(0xa49b2446a02c645bf419f995b67091253a04a259 == Sha1.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
+ htest.assertEqualHash(Sha1, "da39a3ee5e6b4b0d3255bfef95601890afd80709", "");
+ htest.assertEqualHash(Sha1, "a9993e364706816aba3e25717850c26c9cd0d89d", "abc");
+ htest.assertEqualHash(Sha1, "a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
}
test "sha1 streaming" {
var h = Sha1.init();
+ var out: [20]u8 = undefined;
- debug.assert(0xda39a3ee5e6b4b0d3255bfef95601890afd80709 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual("da39a3ee5e6b4b0d3255bfef95601890afd80709", out[0..]);
h.reset();
h.update("abc");
- debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == h.final());
+ h.final(out[0..]);
+ htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]);
h.reset();
h.update("a");
h.update("b");
h.update("c");
- debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == h.final());
+ h.final(out[0..]);
+ htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]);
}
diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig
index cfe4640fe788109ae78e22221283a65c39ef2938..6712a880a99608a21ab57cdfef9f50a924320e00 100644
--- a/std/crypto/sha2.zig
+++ b/std/crypto/sha2.zig
@@ -3,6 +3,7 @@ const math = @import("../math/index.zig");
const endian = @import("../endian.zig");
const debug = @import("../debug/index.zig");
const builtin = @import("builtin");
+const htest = @import("test.zig");
/////////////////////
// Sha224 + Sha256
@@ -57,7 +58,6 @@ pub const Sha256 = Sha2_32(Sha256Params);
fn Sha2_32(comptime params: Sha2Params32) -> type { return struct {
const Self = this;
- const ReturnType = @IntType(false, params.out_len);
s: [8]u32,
// Streaming Cache
@@ -84,10 +84,10 @@ fn Sha2_32(comptime params: Sha2Params32) -> type { return struct {
d.total_len = 0;
}
- pub fn hash(b: []const u8) -> ReturnType {
+ pub fn hash(b: []const u8, out: []u8) {
var d = Self.init();
d.update(b);
- return d.final();
+ d.final(out);
}
pub fn update(d: &Self, b: []const u8) {
@@ -114,7 +114,9 @@ fn Sha2_32(comptime params: Sha2Params32) -> type { return struct {
d.total_len += b.len;
}
- pub fn final(d: &Self) -> ReturnType {
+ pub fn final(d: &Self, out: []u8) {
+ debug.assert(out.len >= params.out_len / 8);
+
// The buffer here will never be completely full.
mem.set(u8, d.buf[d.buf_len..], 0);
@@ -142,14 +144,9 @@ fn Sha2_32(comptime params: Sha2Params32) -> type { return struct {
// May truncate for possible 224 output
const rr = d.s[0 .. params.out_len / 32];
- var j: u8 = u8(rr.len - 1) * 32;
- var r: ReturnType = 0;
- for (rr) |p| {
- r |= ReturnType(p) << j;
- j -%= 32;
+ for (rr) |s, j| {
+ mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Big);
}
-
- return endian.swapIfBe(ReturnType, r);
}
fn round(d: &Self, b: []const u8) {
@@ -275,9 +272,9 @@ test "sha224 single" {
return;
}
- debug.assert(0xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f == Sha224.hash(""));
- debug.assert(0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 == Sha224.hash("abc"));
- debug.assert(0xc97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3 == Sha224.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
+ htest.assertEqualHash(Sha224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", "");
+ htest.assertEqualHash(Sha224, "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc");
+ htest.assertEqualHash(Sha224, "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
}
test "sha224 streaming" {
@@ -287,18 +284,22 @@ test "sha224 streaming" {
}
var h = Sha224.init();
+ var out: [28]u8 = undefined;
- debug.assert(0xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f == h.final());
+ h.final(out[0..]);
+ htest.assertEqual("d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", out[0..]);
h.reset();
h.update("abc");
- debug.assert(0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual("23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", out[0..]);
h.reset();
h.update("a");
h.update("b");
h.update("c");
- debug.assert(0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual("23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", out[0..]);
}
test "sha256 single" {
@@ -307,9 +308,9 @@ test "sha256 single" {
return;
}
- debug.assert(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == Sha256.hash(""));
- debug.assert(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad == Sha256.hash("abc"));
- debug.assert(0xcf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1 == Sha256.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
+ htest.assertEqualHash(Sha256, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "");
+ htest.assertEqualHash(Sha256, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc");
+ htest.assertEqualHash(Sha256, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
}
test "sha256 streaming" {
@@ -319,18 +320,22 @@ test "sha256 streaming" {
}
var h = Sha256.init();
+ var out: [32]u8 = undefined;
- debug.assert(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", out[0..]);
h.reset();
h.update("abc");
- debug.assert(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad == h.final());
+ h.final(out[0..]);
+ htest.assertEqual("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", out[0..]);
h.reset();
h.update("a");
h.update("b");
h.update("c");
- debug.assert(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad == h.final());
+ h.final(out[0..]);
+ htest.assertEqual("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", out[0..]);
}
@@ -387,7 +392,6 @@ pub const Sha512 = Sha2_64(Sha512Params);
fn Sha2_64(comptime params: Sha2Params64) -> type { return struct {
const Self = this;
- const ReturnType = @IntType(false, params.out_len);
const u9 = @IntType(false, 9);
s: [8]u64,
@@ -415,10 +419,10 @@ fn Sha2_64(comptime params: Sha2Params64) -> type { return struct {
d.total_len = 0;
}
- pub fn hash(b: []const u8) -> ReturnType {
+ pub fn hash(b: []const u8, out: []u8) {
var d = Self.init();
d.update(b);
- return d.final();
+ d.final(out);
}
pub fn update(d: &Self, b: []const u8) {
@@ -445,7 +449,9 @@ fn Sha2_64(comptime params: Sha2Params64) -> type { return struct {
d.total_len += b.len;
}
- pub fn final(d: &Self) -> ReturnType {
+ pub fn final(d: &Self, out: []u8) {
+ debug.assert(out.len >= params.out_len / 8);
+
// The buffer here will never be completely full.
mem.set(u8, d.buf[d.buf_len..], 0);
@@ -473,14 +479,9 @@ fn Sha2_64(comptime params: Sha2Params64) -> type { return struct {
// May truncate for possible 384 output
const rr = d.s[0 .. params.out_len / 64];
- var j: u9 = u9(rr.len - 1) * 64;
- var r: ReturnType = 0;
- for (rr) |p| {
- r |= ReturnType(p) << j;
- j -%= 64;
+ for (rr) |s, j| {
+ mem.writeInt(out[8*j .. 8*j + 8], s, builtin.Endian.Big);
}
-
- return endian.swapIfBe(ReturnType, r);
}
fn round(d: &Self, b: []const u8) {
@@ -626,14 +627,14 @@ test "sha384 single" {
return;
}
- const h1 = 0x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b;
- debug.assert(h1 == Sha384.hash(""));
+ const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b";
+ htest.assertEqualHash(Sha384, h1, "");
- const h2 = 0xcb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7;
- debug.assert(h2 == Sha384.hash("abc"));
+ const h2 = "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7";
+ htest.assertEqualHash(Sha384, h2, "abc");
- const h3 = 0x09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039;
- debug.assert(h3 == Sha384.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
+ const h3 = "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039";
+ htest.assertEqualHash(Sha384, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
}
test "sha384 streaming" {
@@ -643,21 +644,25 @@ test "sha384 streaming" {
}
var h = Sha384.init();
+ var out: [48]u8 = undefined;
- const h1 = 0x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b;
- debug.assert(h1 == h.final());
+ const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b";
+ h.final(out[0..]);
+ htest.assertEqual(h1, out[0..]);
- const h2 = 0xcb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7;
+ const h2 = "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7";
h.reset();
h.update("abc");
- debug.assert(h2 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
h.reset();
h.update("a");
h.update("b");
h.update("c");
- debug.assert(h2 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
}
test "sha512 single" {
@@ -666,14 +671,14 @@ test "sha512 single" {
return;
}
- const h1 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e;
- debug.assert(h1 == Sha512.hash(""));
+ const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e";
+ htest.assertEqualHash(Sha512, h1, "");
- const h2 = 0xddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f;
- debug.assert(h2 == Sha512.hash("abc"));
+ const h2 = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f";
+ htest.assertEqualHash(Sha512, h2, "abc");
- const h3 = 0x8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909;
- debug.assert(h3 == Sha512.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"));
+ const h3 = "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909";
+ htest.assertEqualHash(Sha512, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
}
test "sha512 streaming" {
@@ -683,19 +688,23 @@ test "sha512 streaming" {
}
var h = Sha512.init();
+ var out: [64]u8 = undefined;
- const h1 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e;
- debug.assert(h1 == h.final());
+ const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e";
+ h.final(out[0..]);
+ htest.assertEqual(h1, out[0..]);
- const h2 = 0xddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f;
+ const h2 = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f";
h.reset();
h.update("abc");
- debug.assert(h2 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
h.reset();
h.update("a");
h.update("b");
h.update("c");
- debug.assert(h2 == h.final());
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
}
--
2.54.0
From 73b4f098457e141854c26f5f43b2858909c831a3 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Wed, 17 Jan 2018 00:20:20 +1300
Subject: [PATCH 22/25] Add crypto internal test functions
---
std/crypto/test.zig | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 std/crypto/test.zig
diff --git a/std/crypto/test.zig b/std/crypto/test.zig
new file mode 100644
index 0000000000000000000000000000000000000000..a357988901785a5a46b4ee5f8e85a3ee518f8385
--- /dev/null
+++ b/std/crypto/test.zig
@@ -0,0 +1,22 @@
+const debug = @import("../debug/index.zig");
+const mem = @import("../mem.zig");
+const fmt = @import("../fmt/index.zig");
+
+// Hash using the specified hasher `H` asserting `expected == H(input)`.
+pub fn assertEqualHash(comptime Hasher: var, comptime expected: []const u8, input: []const u8) {
+ var h: [expected.len / 2]u8 = undefined;
+ Hasher.hash(input, h[0..]);
+
+ assertEqual(expected, h);
+}
+
+// Assert `expected` == `input` where `input` is a bytestring.
+pub fn assertEqual(comptime expected: []const u8, input: []const u8) {
+ var expected_bytes: [expected.len / 2]u8 = undefined;
+ for (expected_bytes) |*r, i| {
+ *r = fmt.parseInt(u8, expected[2*i .. 2*i+2], 16) catch unreachable;
+ }
+
+ debug.assert(mem.eql(u8, expected_bytes, input));
+}
+
--
2.54.0
From 821cbd7a1b5374f9c4b35e11c75400fdf8d92e48 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 16 Jan 2018 15:01:02 +0100
Subject: [PATCH 23/25] Output "undefined" on uninitialized variables
---
src/translate_c.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/translate_c.cpp b/src/translate_c.cpp
index 2d0772aeaacfd7dbf754ff77041af6a332fa2898..c04e07c9a4334c6165ebc06853a990279e9e0425 100644
--- a/src/translate_c.cpp
+++ b/src/translate_c.cpp
@@ -1955,6 +1955,8 @@ static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt
if (init_node == nullptr)
return ErrorUnexpected;
+ } else {
+ init_node = trans_create_node(c, NodeTypeUndefinedLiteral);
}
AstNode *type_node = trans_qual_type(c, qual_type, stmt->getLocStart());
if (type_node == nullptr)
--
2.54.0
From f59dcc55463dcf27ce05a5b507dcc7bf26fc60af Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 16 Jan 2018 15:21:48 +0100
Subject: [PATCH 24/25] Fixed tests for undefined variables
---
test/translate_c.zig | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/test/translate_c.zig b/test/translate_c.zig
index cdf900c8b2f3bbad7492110ff56d5aa95a5a0470..4798d5652c96e7803335467c2b4f13dd0ad6d7ba 100644
--- a/test/translate_c.zig
+++ b/test/translate_c.zig
@@ -408,7 +408,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\}
,
\\pub export fn s(a: c_int, b: c_int) -> c_int {
- \\ var c: c_int;
+ \\ var c: c_int = undefined;
\\ c = (a + b);
\\ c = (a - b);
\\ c = (a * b);
@@ -416,7 +416,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ c = @rem(a, b);
\\}
\\pub export fn u(a: c_uint, b: c_uint) -> c_uint {
- \\ var c: c_uint;
+ \\ var c: c_uint = undefined;
\\ c = (a +% b);
\\ c = (a -% b);
\\ c = (a *% b);
@@ -460,7 +460,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
,
\\pub export fn max(_arg_a: c_int) -> c_int {
\\ var a = _arg_a;
- \\ var tmp: c_int;
+ \\ var tmp: c_int = undefined;
\\ tmp = a;
\\ a = tmp;
\\}
@@ -473,8 +473,8 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\}
,
\\pub export fn max(a: c_int) {
- \\ var b: c_int;
- \\ var c: c_int;
+ \\ var b: c_int = undefined;
+ \\ var c: c_int = undefined;
\\ c = x: {
\\ const _tmp = a;
\\ b = _tmp;
--
2.54.0
From 3974b7d31d5b163989255c9a164a8db15bd0ddf1 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 16 Jan 2018 15:48:28 +0100
Subject: [PATCH 25/25] translate_c can now translate if statements on integers
and floats
---
src/translate_c.cpp | 88 ++++++++++++++++++++++++++++++++++++++++----
test/translate_c.zig | 21 +++++++++++
2 files changed, 102 insertions(+), 7 deletions(-)
diff --git a/src/translate_c.cpp b/src/translate_c.cpp
index c04e07c9a4334c6165ebc06853a990279e9e0425..2c00038a488ff8cafda5aa009d910419e12edb4a 100644
--- a/src/translate_c.cpp
+++ b/src/translate_c.cpp
@@ -2217,12 +2217,6 @@ static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt *
// if (c) t else e
AstNode *if_node = trans_create_node(c, NodeTypeIfBoolExpr);
- // TODO: condition != 0
- AstNode *condition_node = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
- if (condition_node == nullptr)
- return nullptr;
- if_node->data.if_bool_expr.condition = condition_node;
-
TransScope *then_scope = trans_stmt(c, scope, stmt->getThen(), &if_node->data.if_bool_expr.then_block);
if (then_scope == nullptr)
return nullptr;
@@ -2233,7 +2227,87 @@ static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt *
return nullptr;
}
- return if_node;
+ AstNode *condition_node = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
+ if (condition_node == nullptr)
+ return nullptr;
+
+ switch (condition_node->type) {
+ case NodeTypeBinOpExpr:
+ switch (condition_node->data.bin_op_expr.bin_op) {
+ case BinOpTypeBoolOr:
+ case BinOpTypeBoolAnd:
+ case BinOpTypeCmpEq:
+ case BinOpTypeCmpNotEq:
+ case BinOpTypeCmpLessThan:
+ case BinOpTypeCmpGreaterThan:
+ case BinOpTypeCmpLessOrEq:
+ case BinOpTypeCmpGreaterOrEq:
+ if_node->data.if_bool_expr.condition = condition_node;
+ return if_node;
+ default:
+ goto convert_to_bitcast;
+ }
+
+ case NodeTypePrefixOpExpr:
+ switch (condition_node->data.prefix_op_expr.prefix_op) {
+ case PrefixOpBoolNot:
+ if_node->data.if_bool_expr.condition = condition_node;
+ return if_node;
+ default:
+ goto convert_to_bitcast;
+ }
+
+ case NodeTypeBoolLiteral:
+ if_node->data.if_bool_expr.condition = condition_node;
+ return if_node;
+
+ default: {
+ // In Zig, float, int and pointer does not work in if statements.
+ // To make it work, we bitcast any value we get to an int of the right size
+ // and comp it to 0
+ // TODO: This doesn't work for pointers, as they become nullable on
+ // translate
+ // c: if (cond) { }
+ // zig: {
+ // zig: const _tmp = cond;
+ // zig: if (@bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0) { }
+ // zig: }
+ convert_to_bitcast:
+ TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
+
+ // const _tmp = cond;
+ // TODO: avoid name collisions with generated variable names
+ Buf* tmp_var_name = buf_create_from_str("_tmp");
+ AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, condition_node);
+ child_scope->node->data.block.statements.append(tmp_var_decl);
+
+ // @sizeOf(@typeOf(_tmp)) * 8
+ AstNode *typeof_tmp = trans_create_node_builtin_fn_call_str(c, "typeOf");
+ typeof_tmp->data.fn_call_expr.params.append(trans_create_node_symbol(c, tmp_var_name));
+ AstNode *sizeof_tmp = trans_create_node_builtin_fn_call_str(c, "sizeOf");
+ sizeof_tmp->data.fn_call_expr.params.append(typeof_tmp);
+ AstNode *sizeof_tmp_in_bits = trans_create_node_bin_op(
+ c, sizeof_tmp, BinOpTypeMult,
+ trans_create_node_unsigned_negative(c, 8, false));
+
+ // @IntType(false, @sizeOf(@typeOf(_tmp)) * 8)
+ AstNode *int_type = trans_create_node_builtin_fn_call_str(c, "IntType");
+ int_type->data.fn_call_expr.params.append(trans_create_node_bool(c, false));
+ int_type->data.fn_call_expr.params.append(sizeof_tmp_in_bits);
+
+ // @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp)
+ AstNode *bit_cast = trans_create_node_builtin_fn_call_str(c, "bitCast");
+ bit_cast->data.fn_call_expr.params.append(int_type);
+ bit_cast->data.fn_call_expr.params.append(trans_create_node_symbol(c, tmp_var_name));
+
+ // if (@bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0) { }
+ AstNode *not_eql_zero = trans_create_node_bin_op(c, bit_cast, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false));
+ if_node->data.if_bool_expr.condition = not_eql_zero;
+ child_scope->node->data.block.statements.append(if_node);
+
+ return child_scope->node;
+ }
+ }
}
static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *scope, const CallExpr *stmt) {
diff --git a/test/translate_c.zig b/test/translate_c.zig
index 4798d5652c96e7803335467c2b4f13dd0ad6d7ba..8e6a8dda2cd64c7a88a21d3809a0de7c1ec20b16 100644
--- a/test/translate_c.zig
+++ b/test/translate_c.zig
@@ -1114,4 +1114,25 @@ pub fn addCases(cases: &tests.TranslateCContext) {
,
\\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast(&NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr(&NRF_GPIO_Type, NRF_GPIO_BASE) else (&NRF_GPIO_Type)(NRF_GPIO_BASE);
);
+
+ cases.add("if on int",
+ \\int if_int(int i) {
+ \\ if (i) {
+ \\ return 0;
+ \\ } else {
+ \\ return 1;
+ \\ }
+ \\}
+ ,
+ \\pub fn if_int(i: c_int) -> c_int {
+ \\ {
+ \\ const _tmp = i;
+ \\ if (@bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0) {
+ \\ return 0;
+ \\ } else {
+ \\ return 1;
+ \\ };
+ \\ };
+ \\}
+ );
}
--
2.54.0