From 581edd643fb18a66c472f77e2f8cd3f4cea524a2 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 29 Jan 2019 21:47:26 -0500
Subject: backport copy elision changes
This commit contains everything from the copy-elision-2
branch that does not have to do with copy elision directly,
but is generally useful for master branch.
* All const values know their parents, when applicable, not
just structs and unions.
* Null pointers in const values are represented explicitly,
rather than as a HardCodedAddr value of 0.
* Rename "maybe" to "optional" in various code locations.
* Separate DeclVarSrc and DeclVarGen
* Separate PtrCastSrc and PtrCastGen
* Separate CmpxchgSrc and CmpxchgGen
* Represent optional error set as an integer, using the 0 value.
In a const value, it uses nullptr.
* Introduce type_has_one_possible_value and use it where applicable.
* Fix debug builds not setting memory to 0xaa when storing
undefined.
* Separate the type of a variable from the const value of a variable.
* Use copy_const_val where appropriate.
* Rearrange structs to pack data more efficiently.
* Move test/cases/* to test/behavior/*
* Use `std.debug.assertOrPanic` in behavior tests instead of
`std.debug.assert`.
* Fix outdated slice syntax in docs.
---
src/codegen.cpp | 385 +++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 285 insertions(+), 100 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 47f2aa103f..2f360735dd 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -313,6 +313,8 @@ static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char *
static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const char *name);
static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name);
static void generate_error_name_table(CodeGen *g);
+static bool value_is_all_undef(ConstExprValue *const_val);
+static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr);
static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) {
unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name));
@@ -461,6 +463,21 @@ static void maybe_import_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkag
}
}
+static bool cc_want_sret_attr(CallingConvention cc) {
+ switch (cc) {
+ case CallingConventionNaked:
+ zig_unreachable();
+ case CallingConventionC:
+ case CallingConventionCold:
+ case CallingConventionStdcall:
+ return true;
+ case CallingConventionAsync:
+ case CallingConventionUnspecified:
+ return false;
+ }
+ zig_unreachable();
+}
+
static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
if (fn_table_entry->llvm_value)
return fn_table_entry->llvm_value;
@@ -598,9 +615,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
} else if (type_is_codegen_pointer(return_type)) {
addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");
} else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
- addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull");
- if (cc == CallingConventionC) {
+ addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
+ if (cc_want_sret_attr(cc)) {
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "noalias");
}
init_gen_i = 1;
@@ -2200,10 +2217,10 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) {
assert(variable);
assert(variable->value_ref);
- if (!handle_is_ptr(variable->value->type)) {
+ if (!handle_is_ptr(variable->var_type)) {
clear_debug_source_node(g);
- gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index), variable->value_ref,
- variable->align_bytes, false);
+ gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index),
+ variable->value_ref, variable->align_bytes, false);
}
if (variable->decl_node) {
@@ -2961,7 +2978,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
}
static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable,
- IrInstructionPtrCast *instruction)
+ IrInstructionPtrCastGen *instruction)
{
ZigType *wanted_type = instruction->base.value.type;
if (!type_has_bits(wanted_type)) {
@@ -3149,11 +3166,11 @@ static LLVMValueRef ir_render_bool_not(CodeGen *g, IrExecutable *executable, IrI
}
static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
- IrInstructionDeclVar *decl_var_instruction)
+ IrInstructionDeclVarGen *decl_var_instruction)
{
ZigVar *var = decl_var_instruction->var;
- if (!type_has_bits(var->value->type))
+ if (!type_has_bits(var->var_type))
return nullptr;
if (var->ref_count == 0 && g->build_mode != BuildModeDebug)
@@ -3161,34 +3178,16 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
IrInstruction *init_value = decl_var_instruction->init_value;
- bool have_init_expr = false;
-
- ConstExprValue *const_val = &init_value->value;
- if (const_val->special == ConstValSpecialRuntime || const_val->special == ConstValSpecialStatic)
- have_init_expr = true;
+ bool have_init_expr = !value_is_all_undef(&init_value->value);
if (have_init_expr) {
- assert(var->value->type == init_value->value.type);
- ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false,
+ ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->var_type, false, false,
PtrLenSingle, var->align_bytes, 0, 0);
LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value);
gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val);
- } else {
- bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base);
- if (want_safe) {
- ZigType *usize = g->builtin_types.entry_usize;
- uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, var->value->type->type_ref);
- assert(size_bytes > 0);
-
- assert(var->align_bytes > 0);
-
- // memset uninitialized memory to 0xa
- LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
- LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
- LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, var->value_ref, ptr_u8, "");
- LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false);
- ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, var->align_bytes, false);
- }
+ } else if (ir_want_runtime_safety(g, &decl_var_instruction->base)) {
+ uint32_t align_bytes = (var->align_bytes == 0) ? get_abi_alignment(g, var->var_type) : var->align_bytes;
+ gen_undef_init(g, align_bytes, var->var_type, var->value_ref);
}
gen_var_debug_decl(g, var);
@@ -3225,21 +3224,75 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
}
-static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) {
- LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
- LLVMValueRef value = ir_llvm_value(g, instruction->value);
+static bool value_is_all_undef(ConstExprValue *const_val) {
+ switch (const_val->special) {
+ case ConstValSpecialRuntime:
+ return false;
+ case ConstValSpecialUndef:
+ return true;
+ case ConstValSpecialStatic:
+ if (const_val->type->id == ZigTypeIdStruct) {
+ for (size_t i = 0; i < const_val->type->data.structure.src_field_count; i += 1) {
+ if (!value_is_all_undef(&const_val->data.x_struct.fields[i]))
+ return false;
+ }
+ return true;
+ } else if (const_val->type->id == ZigTypeIdArray) {
+ switch (const_val->data.x_array.special) {
+ case ConstArraySpecialUndef:
+ return true;
+ case ConstArraySpecialBuf:
+ return false;
+ case ConstArraySpecialNone:
+ for (size_t i = 0; i < const_val->type->data.array.len; i += 1) {
+ if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i]))
+ return false;
+ }
+ return true;
+ }
+ zig_unreachable();
+ } else {
+ return false;
+ }
+ }
+ zig_unreachable();
+}
- assert(instruction->ptr->value.type->id == ZigTypeIdPointer);
- ZigType *ptr_type = instruction->ptr->value.type;
+static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) {
+ assert(type_has_bits(value_type));
+ uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, value_type->type_ref);
+ assert(size_bytes > 0);
+ assert(ptr_align_bytes > 0);
+ // memset uninitialized memory to 0xaa
+ LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
+ LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
+ LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, "");
+ ZigType *usize = g->builtin_types.entry_usize;
+ LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false);
+ ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false);
+}
- gen_assign_raw(g, ptr, ptr_type, value);
+static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) {
+ ZigType *ptr_type = instruction->ptr->value.type;
+ assert(ptr_type->id == ZigTypeIdPointer);
+ if (!type_has_bits(ptr_type))
+ return nullptr;
+ bool have_init_expr = !value_is_all_undef(&instruction->value->value);
+ if (have_init_expr) {
+ LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
+ LLVMValueRef value = ir_llvm_value(g, instruction->value);
+ gen_assign_raw(g, ptr, ptr_type, value);
+ } else if (ir_want_runtime_safety(g, &instruction->base)) {
+ gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value.type,
+ ir_llvm_value(g, instruction->ptr));
+ }
return nullptr;
}
static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrInstructionVarPtr *instruction) {
ZigVar *var = instruction->var;
- if (type_has_bits(var->value->type)) {
+ if (type_has_bits(var->var_type)) {
assert(var->value_ref);
return var->value_ref;
} else {
@@ -3553,7 +3606,8 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab
LLVMPositionBuilderAtEnd(g->builder, ok_block);
}
- LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_union_index, "");
+ LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr,
+ union_type->data.unionation.gen_union_index, "");
LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, "");
return bitcasted_union_field_ptr;
}
@@ -3715,8 +3769,8 @@ static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueR
if (child_type->zero_bits) {
return maybe_handle;
} else {
- bool maybe_is_ptr = type_is_codegen_pointer(child_type);
- if (maybe_is_ptr) {
+ bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet;
+ if (is_scalar) {
return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(maybe_type->type_ref), "");
} else {
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, "");
@@ -3731,17 +3785,17 @@ static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable
return gen_non_null_bit(g, instruction->value->value.type, ir_llvm_value(g, instruction->value));
}
-static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
- IrInstructionUnwrapOptional *instruction)
+static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *executable,
+ IrInstructionOptionalUnwrapPtr *instruction)
{
- ZigType *ptr_type = instruction->value->value.type;
+ ZigType *ptr_type = instruction->base_ptr->value.type;
assert(ptr_type->id == ZigTypeIdPointer);
ZigType *maybe_type = ptr_type->data.pointer.child_type;
assert(maybe_type->id == ZigTypeIdOptional);
ZigType *child_type = maybe_type->data.maybe.child_type;
- LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value);
- LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type);
+ LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->base_ptr);
if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) {
+ LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type);
LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle);
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk");
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail");
@@ -3755,8 +3809,8 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
if (child_type->zero_bits) {
return nullptr;
} else {
- bool maybe_is_ptr = type_is_codegen_pointer(child_type);
- if (maybe_is_ptr) {
+ bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet;
+ if (is_scalar) {
return maybe_ptr;
} else {
LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type, ptr_type);
@@ -4174,7 +4228,7 @@ static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed)
zig_unreachable();
}
-static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchg *instruction) {
+static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchgGen *instruction) {
LLVMValueRef ptr_val = ir_llvm_value(g, instruction->ptr);
LLVMValueRef cmp_val = ir_llvm_value(g, instruction->cmp_value);
LLVMValueRef new_val = ir_llvm_value(g, instruction->new_value);
@@ -4189,18 +4243,18 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn
assert(maybe_type->id == ZigTypeIdOptional);
ZigType *child_type = maybe_type->data.maybe.child_type;
- if (type_is_codegen_pointer(child_type)) {
+ if (!handle_is_ptr(maybe_type)) {
LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, "");
return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, "");
}
assert(instruction->tmp_ptr != nullptr);
- assert(type_has_bits(instruction->type));
+ assert(type_has_bits(child_type));
LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, "");
- gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val);
+ gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val);
LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, "");
LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, "");
@@ -4351,6 +4405,7 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
assert(array_type->data.structure.is_slice);
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
+ assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(tmp_struct_ptr))) == LLVMStructTypeKind);
size_t ptr_index = array_type->data.structure.fields[slice_ptr_index].gen_index;
assert(ptr_index != SIZE_MAX);
@@ -4540,12 +4595,14 @@ static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrI
return LLVMBuildICmp(g->builder, LLVMIntNE, err_val, zero, "");
}
-static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrCode *instruction) {
- ZigType *ptr_type = instruction->value->value.type;
+static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable,
+ IrInstructionUnwrapErrCode *instruction)
+{
+ ZigType *ptr_type = instruction->err_union->value.type;
assert(ptr_type->id == ZigTypeIdPointer);
ZigType *err_union_type = ptr_type->data.pointer.child_type;
ZigType *payload_type = err_union_type->data.error_union.payload_type;
- LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
+ LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->err_union);
LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type);
if (type_has_bits(payload_type)) {
@@ -4556,7 +4613,13 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab
}
}
-static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrPayload *instruction) {
+static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable,
+ IrInstructionUnwrapErrPayload *instruction)
+{
+ bool want_safety = ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on &&
+ g->errors_by_index.length > 1;
+ if (!want_safety && !type_has_bits(instruction->base.value.type))
+ return nullptr;
ZigType *ptr_type = instruction->value->value.type;
assert(ptr_type->id == ZigTypeIdPointer);
ZigType *err_union_type = ptr_type->data.pointer.child_type;
@@ -4568,7 +4631,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu
return err_union_handle;
}
- if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->errors_by_index.length > 1) {
+ if (want_safety) {
LLVMValueRef err_val;
if (type_has_bits(payload_type)) {
LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
@@ -4607,7 +4670,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I
}
LLVMValueRef payload_val = ir_llvm_value(g, instruction->value);
- if (type_is_codegen_pointer(child_type)) {
+ if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) {
return payload_val;
}
@@ -5184,12 +5247,15 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdToBytes:
case IrInstructionIdEnumToInt:
case IrInstructionIdCheckRuntimeScope:
+ case IrInstructionIdDeclVarSrc:
+ case IrInstructionIdPtrCastSrc:
+ case IrInstructionIdCmpxchgSrc:
zig_unreachable();
+ case IrInstructionIdDeclVarGen:
+ return ir_render_decl_var(g, executable, (IrInstructionDeclVarGen *)instruction);
case IrInstructionIdReturn:
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
- case IrInstructionIdDeclVar:
- return ir_render_decl_var(g, executable, (IrInstructionDeclVar *)instruction);
case IrInstructionIdBinOp:
return ir_render_bin_op(g, executable, (IrInstructionBinOp *)instruction);
case IrInstructionIdCast:
@@ -5220,8 +5286,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);
case IrInstructionIdTestNonNull:
return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction);
- case IrInstructionIdUnwrapOptional:
- return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapOptional *)instruction);
+ case IrInstructionIdOptionalUnwrapPtr:
+ return ir_render_optional_unwrap_ptr(g, executable, (IrInstructionOptionalUnwrapPtr *)instruction);
case IrInstructionIdClz:
return ir_render_clz(g, executable, (IrInstructionClz *)instruction);
case IrInstructionIdCtz:
@@ -5236,8 +5302,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_ref(g, executable, (IrInstructionRef *)instruction);
case IrInstructionIdErrName:
return ir_render_err_name(g, executable, (IrInstructionErrName *)instruction);
- case IrInstructionIdCmpxchg:
- return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchg *)instruction);
+ case IrInstructionIdCmpxchgGen:
+ return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchgGen *)instruction);
case IrInstructionIdFence:
return ir_render_fence(g, executable, (IrInstructionFence *)instruction);
case IrInstructionIdTruncate:
@@ -5278,8 +5344,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction);
case IrInstructionIdUnionInit:
return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction);
- case IrInstructionIdPtrCast:
- return ir_render_ptr_cast(g, executable, (IrInstructionPtrCast *)instruction);
+ case IrInstructionIdPtrCastGen:
+ return ir_render_ptr_cast(g, executable, (IrInstructionPtrCastGen *)instruction);
case IrInstructionIdBitCast:
return ir_render_bit_cast(g, executable, (IrInstructionBitCast *)instruction);
case IrInstructionIdWidenOrShorten:
@@ -5377,6 +5443,9 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) {
static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index);
static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index);
static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val);
+static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val);
+static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val);
+static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val);
static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) {
switch (parent->id) {
@@ -5387,6 +5456,12 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent
case ConstParentIdStruct:
return gen_const_ptr_struct_recursive(g, parent->data.p_struct.struct_val,
parent->data.p_struct.field_index);
+ case ConstParentIdErrUnionCode:
+ return gen_const_ptr_err_union_code_recursive(g, parent->data.p_err_union_code.err_union_val);
+ case ConstParentIdErrUnionPayload:
+ return gen_const_ptr_err_union_payload_recursive(g, parent->data.p_err_union_payload.err_union_val);
+ case ConstParentIdOptionalPayload:
+ return gen_const_ptr_optional_payload_recursive(g, parent->data.p_optional_payload.optional_val);
case ConstParentIdArray:
return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val,
parent->data.p_array.elem_index);
@@ -5402,7 +5477,7 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent
static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index) {
expand_undef_array(g, array_const_val);
- ConstParent *parent = &array_const_val->data.x_array.data.s_none.parent;
+ ConstParent *parent = &array_const_val->parent;
LLVMValueRef base_ptr = gen_parent_ptr(g, array_const_val, parent);
LLVMTypeKind el_type = LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(base_ptr)));
@@ -5427,7 +5502,7 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *ar
}
static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index) {
- ConstParent *parent = &struct_const_val->data.x_struct.parent;
+ ConstParent *parent = &struct_const_val->parent;
LLVMValueRef base_ptr = gen_parent_ptr(g, struct_const_val, parent);
ZigType *u32 = g->builtin_types.entry_u32;
@@ -5438,8 +5513,44 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s
return LLVMConstInBoundsGEP(base_ptr, indices, 2);
}
+static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val) {
+ ConstParent *parent = &err_union_const_val->parent;
+ LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent);
+
+ ZigType *u32 = g->builtin_types.entry_u32;
+ LLVMValueRef indices[] = {
+ LLVMConstNull(u32->type_ref),
+ LLVMConstInt(u32->type_ref, err_union_err_index, false),
+ };
+ return LLVMConstInBoundsGEP(base_ptr, indices, 2);
+}
+
+static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val) {
+ ConstParent *parent = &err_union_const_val->parent;
+ LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent);
+
+ ZigType *u32 = g->builtin_types.entry_u32;
+ LLVMValueRef indices[] = {
+ LLVMConstNull(u32->type_ref),
+ LLVMConstInt(u32->type_ref, err_union_payload_index, false),
+ };
+ return LLVMConstInBoundsGEP(base_ptr, indices, 2);
+}
+
+static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val) {
+ ConstParent *parent = &optional_const_val->parent;
+ LLVMValueRef base_ptr = gen_parent_ptr(g, optional_const_val, parent);
+
+ ZigType *u32 = g->builtin_types.entry_u32;
+ LLVMValueRef indices[] = {
+ LLVMConstNull(u32->type_ref),
+ LLVMConstInt(u32->type_ref, maybe_child_index, false),
+ };
+ return LLVMConstInBoundsGEP(base_ptr, indices, 2);
+}
+
static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) {
- ConstParent *parent = &union_const_val->data.x_union.parent;
+ ConstParent *parent = &union_const_val->parent;
LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent);
ZigType *u32 = g->builtin_types.entry_u32;
@@ -5609,6 +5720,63 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
render_const_val_global(g, const_val, "");
return ptr_val;
}
+ case ConstPtrSpecialBaseErrorUnionCode:
+ {
+ render_const_val_global(g, const_val, name);
+ ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_code.err_union_val;
+ assert(err_union_const_val->type->id == ZigTypeIdErrorUnion);
+ if (err_union_const_val->type->zero_bits) {
+ // make this a null pointer
+ ZigType *usize = g->builtin_types.entry_usize;
+ const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
+ const_val->type->type_ref);
+ render_const_val_global(g, const_val, "");
+ return const_val->global_refs->llvm_value;
+ }
+ LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_code_recursive(g, err_union_const_val);
+ LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
+ const_val->global_refs->llvm_value = ptr_val;
+ render_const_val_global(g, const_val, "");
+ return ptr_val;
+ }
+ case ConstPtrSpecialBaseErrorUnionPayload:
+ {
+ render_const_val_global(g, const_val, name);
+ ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_payload.err_union_val;
+ assert(err_union_const_val->type->id == ZigTypeIdErrorUnion);
+ if (err_union_const_val->type->zero_bits) {
+ // make this a null pointer
+ ZigType *usize = g->builtin_types.entry_usize;
+ const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
+ const_val->type->type_ref);
+ render_const_val_global(g, const_val, "");
+ return const_val->global_refs->llvm_value;
+ }
+ LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_payload_recursive(g, err_union_const_val);
+ LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
+ const_val->global_refs->llvm_value = ptr_val;
+ render_const_val_global(g, const_val, "");
+ return ptr_val;
+ }
+ case ConstPtrSpecialBaseOptionalPayload:
+ {
+ render_const_val_global(g, const_val, name);
+ ConstExprValue *optional_const_val = const_val->data.x_ptr.data.base_optional_payload.optional_val;
+ assert(optional_const_val->type->id == ZigTypeIdOptional);
+ if (optional_const_val->type->zero_bits) {
+ // make this a null pointer
+ ZigType *usize = g->builtin_types.entry_usize;
+ const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
+ const_val->type->type_ref);
+ render_const_val_global(g, const_val, "");
+ return const_val->global_refs->llvm_value;
+ }
+ LLVMValueRef uncasted_ptr_val = gen_const_ptr_optional_payload_recursive(g, optional_const_val);
+ LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
+ const_val->global_refs->llvm_value = ptr_val;
+ render_const_val_global(g, const_val, "");
+ return ptr_val;
+ }
case ConstPtrSpecialHardCodedAddr:
{
render_const_val_global(g, const_val, name);
@@ -5621,10 +5789,17 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
}
case ConstPtrSpecialFunction:
return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref);
+ case ConstPtrSpecialNull:
+ return LLVMConstNull(const_val->type->type_ref);
}
zig_unreachable();
}
+static LLVMValueRef gen_const_val_err_set(CodeGen *g, ConstExprValue *const_val, const char *name) {
+ uint64_t value = (const_val->data.x_err_set == nullptr) ? 0 : const_val->data.x_err_set->value;
+ return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref, value, false);
+}
+
static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {
Error err;
@@ -5644,9 +5819,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
case ZigTypeIdInt:
return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint);
case ZigTypeIdErrorSet:
- assert(const_val->data.x_err_set != nullptr);
- return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref,
- const_val->data.x_err_set->value, false);
+ return gen_const_val_err_set(g, const_val, name);
case ZigTypeIdFloat:
switch (type_entry->data.floating.bit_count) {
case 16:
@@ -5680,6 +5853,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false);
} else if (type_is_codegen_pointer(child_type)) {
return gen_const_val_ptr(g, const_val, name);
+ } else if (child_type->id == ZigTypeIdErrorSet) {
+ return gen_const_val_err_set(g, const_val, name);
} else {
LLVMValueRef child_val;
LLVMValueRef maybe_val;
@@ -5914,7 +6089,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
ZigType *err_set_type = type_entry->data.error_union.err_set_type;
if (!type_has_bits(payload_type)) {
assert(type_has_bits(err_set_type));
- uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0;
+ ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set;
+ uint64_t value = (err_set == nullptr) ? 0 : err_set->value;
return LLVMConstInt(g->err_tag_type->type_ref, value, false);
} else if (!type_has_bits(err_set_type)) {
assert(type_has_bits(payload_type));
@@ -5923,8 +6099,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
LLVMValueRef err_tag_value;
LLVMValueRef err_payload_value;
bool make_unnamed_struct;
- if (const_val->data.x_err_union.err) {
- err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err_union.err->value, false);
+ ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set;
+ if (err_set != nullptr) {
+ err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, err_set->value, false);
err_payload_value = LLVMConstNull(payload_type->type_ref);
make_unnamed_struct = false;
} else {
@@ -6130,10 +6307,13 @@ static void do_code_gen(CodeGen *g) {
TldVar *tld_var = g->global_vars.at(i);
ZigVar *var = tld_var->var;
- if (var->value->type->id == ZigTypeIdComptimeFloat) {
+ if (var->var_type->id == ZigTypeIdComptimeFloat) {
// Generate debug info for it but that's it.
- ConstExprValue *const_val = var->value;
+ ConstExprValue *const_val = var->const_value;
assert(const_val->special != ConstValSpecialRuntime);
+ if (const_val->type != var->var_type) {
+ zig_panic("TODO debug info for var with ptr casted value");
+ }
ZigType *var_type = g->builtin_types.entry_f128;
ConstExprValue coerced_value;
coerced_value.special = ConstValSpecialStatic;
@@ -6144,10 +6324,13 @@ static void do_code_gen(CodeGen *g) {
continue;
}
- if (var->value->type->id == ZigTypeIdComptimeInt) {
+ if (var->var_type->id == ZigTypeIdComptimeInt) {
// Generate debug info for it but that's it.
- ConstExprValue *const_val = var->value;
+ ConstExprValue *const_val = var->const_value;
assert(const_val->special != ConstValSpecialRuntime);
+ if (const_val->type != var->var_type) {
+ zig_panic("TODO debug info for var with ptr casted value");
+ }
size_t bits_needed = bigint_bits_needed(&const_val->data.x_bigint);
if (bits_needed < 8) {
bits_needed = 8;
@@ -6158,7 +6341,7 @@ static void do_code_gen(CodeGen *g) {
continue;
}
- if (!type_has_bits(var->value->type))
+ if (!type_has_bits(var->var_type))
continue;
assert(var->decl_node);
@@ -6167,9 +6350,9 @@ static void do_code_gen(CodeGen *g) {
if (var->linkage == VarLinkageExternal) {
LLVMValueRef existing_llvm_var = LLVMGetNamedGlobal(g->module, buf_ptr(&var->name));
if (existing_llvm_var) {
- global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->value->type->type_ref, 0));
+ global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->var_type->type_ref, 0));
} else {
- global_value = LLVMAddGlobal(g->module, var->value->type->type_ref, buf_ptr(&var->name));
+ global_value = LLVMAddGlobal(g->module, var->var_type->type_ref, buf_ptr(&var->name));
// TODO debug info for the extern variable
LLVMSetLinkage(global_value, LLVMExternalLinkage);
@@ -6180,9 +6363,9 @@ static void do_code_gen(CodeGen *g) {
} else {
bool exported = (var->linkage == VarLinkageExport);
const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported));
- render_const_val(g, var->value, mangled_name);
- render_const_val_global(g, var->value, mangled_name);
- global_value = var->value->global_refs->llvm_global;
+ render_const_val(g, var->const_value, mangled_name);
+ render_const_val_global(g, var->const_value, mangled_name);
+ global_value = var->const_value->global_refs->llvm_global;
if (exported) {
LLVMSetLinkage(global_value, LLVMExternalLinkage);
@@ -6194,8 +6377,10 @@ static void do_code_gen(CodeGen *g) {
LLVMSetAlignment(global_value, var->align_bytes);
// TODO debug info for function pointers
- if (var->gen_is_const && var->value->type->id != ZigTypeIdFn) {
- gen_global_var(g, var, var->value->global_refs->llvm_value, var->value->type);
+ // Here we use const_value->type because that's the type of the llvm global,
+ // which we const ptr cast upon use to whatever it needs to be.
+ if (var->gen_is_const && var->const_value->type->id != ZigTypeIdFn) {
+ gen_global_var(g, var, var->const_value->global_refs->llvm_value, var->const_value->type);
}
LLVMSetGlobalConstant(global_value, var->gen_is_const);
@@ -6281,8 +6466,8 @@ static void do_code_gen(CodeGen *g) {
} else if (instruction->id == IrInstructionIdErrWrapCode) {
IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction;
slot = &err_wrap_code_instruction->tmp_ptr;
- } else if (instruction->id == IrInstructionIdCmpxchg) {
- IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction;
+ } else if (instruction->id == IrInstructionIdCmpxchgGen) {
+ IrInstructionCmpxchgGen *cmpxchg_instruction = (IrInstructionCmpxchgGen *)instruction;
slot = &cmpxchg_instruction->tmp_ptr;
} else {
zig_unreachable();
@@ -6304,12 +6489,12 @@ static void do_code_gen(CodeGen *g) {
for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) {
ZigVar *var = fn_table_entry->variable_list.at(var_i);
- if (!type_has_bits(var->value->type)) {
+ if (!type_has_bits(var->var_type)) {
continue;
}
if (ir_get_var_is_comptime(var))
continue;
- switch (type_requires_comptime(g, var->value->type)) {
+ switch (type_requires_comptime(g, var->var_type)) {
case ReqCompTimeInvalid:
zig_unreachable();
case ReqCompTimeYes:
@@ -6319,11 +6504,11 @@ static void do_code_gen(CodeGen *g) {
}
if (var->src_arg_index == SIZE_MAX) {
- var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes);
+ var->value_ref = build_alloca(g, var->var_type, buf_ptr(&var->name), var->align_bytes);
var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1),
- var->value->type->di_type, !g->strip_debug_symbols, 0);
+ var->var_type->di_type, !g->strip_debug_symbols, 0);
} else if (is_c_abi) {
fn_walk_var.data.vars.var = var;
@@ -6333,16 +6518,16 @@ static void do_code_gen(CodeGen *g) {
ZigType *gen_type;
FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index];
- if (handle_is_ptr(var->value->type)) {
+ if (handle_is_ptr(var->var_type)) {
if (gen_info->is_byval) {
- gen_type = var->value->type;
+ gen_type = var->var_type;
} else {
gen_type = gen_info->type;
}
var->value_ref = LLVMGetParam(fn, (unsigned)var->gen_arg_index);
} else {
- gen_type = var->value->type;
- var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes);
+ gen_type = var->var_type;
+ var->value_ref = build_alloca(g, var->var_type, buf_ptr(&var->name), var->align_bytes);
}
if (var->decl_node) {
var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
@@ -7458,9 +7643,9 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) {
ConstExprValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i];
this_val->special = ConstValSpecialStatic;
this_val->type = struct_type;
- this_val->data.x_struct.parent.id = ConstParentIdArray;
- this_val->data.x_struct.parent.data.p_array.array_val = test_fn_array;
- this_val->data.x_struct.parent.data.p_array.elem_index = i;
+ this_val->parent.id = ConstParentIdArray;
+ this_val->parent.data.p_array.array_val = test_fn_array;
+ this_val->parent.data.p_array.elem_index = i;
this_val->data.x_struct.fields = create_const_vals(2);
ConstExprValue *name_field = &this_val->data.x_struct.fields[0];
--
cgit v1.2.3
From 545064c1d9137a603e3e28aa066ce9e65a1ed4b6 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 30 Jan 2019 23:36:52 -0500
Subject: introduce vector type for SIMD
See #903
* create with `@Vector(len, ElemType)`
* only wrapping addition is implemented
This feature is far from complete; this is only the beginning.
---
doc/langref.html.in | 34 ++++++
src-self-hosted/type.zig | 16 +++
src/all_types.hpp | 26 +++++
src/analyze.cpp | 91 ++++++++++++++--
src/analyze.hpp | 1 +
src/codegen.cpp | 47 +++++++-
src/ir.cpp | 213 +++++++++++++++++++++++++++++--------
src/ir_print.cpp | 11 ++
src/zig_llvm.cpp | 13 +++
src/zig_llvm.h | 3 +
std/hash_map.zig | 2 +
test/stage1/behavior/type_info.zig | 16 ++-
12 files changed, 419 insertions(+), 54 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 909c0f5817..e192dbbf76 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -1531,6 +1531,29 @@ test "array initialization with function calls" {
{#code_end#}
{#see_also|for|Slices#}
{#header_close#}
+
+ {#header_open|Vectors#}
+
+ A vector is a group of {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on
+ in parallel using a single instruction ({#link|SIMD#}). Vector types are created with the builtin
+ function {#link|@Vector#}.
+
+
+ TODO talk about C ABI interop
+
+ {#header_open|SIMD#}
+
+ TODO Zig's SIMD abilities are just beginning to be fleshed out. Here are some talking points to update the
+ docs with:
+ * What kind of operations can you do? All the operations on integers and floats? What about mixing scalar and vector?
+ * How to convert to/from vectors/arrays
+ * How to access individual elements from vectors, how to loop over the elements
+ * "shuffle"
+ * Advice on writing high perf software, how to abstract the best way
+
+ {#header_close#}
+ {#header_close#}
+
{#header_open|Pointers#}
Zig has two kinds of pointers:
@@ -6607,6 +6630,17 @@ pub const TypeInfo = union(TypeId) {
expression passed as an argument. The expression is evaluated.
+ {#header_close#}
+
+ {#header_open|@Vector#}
+ {#syntax#}@Vector(comptime len: u32, comptime ElemType: type) type{#endsyntax#}
+
+ This function returns a vector type for {#link|SIMD#}.
+
+
+ {#syntax#}ElemType{#endsyntax#} must be an {#link|integer|Integers#}, a {#link|float|Floats#}, or a
+ {#link|pointer|Pointers#}.
+
{#header_close#}
{#header_close#}
diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig
index aa00bb876d..fa31343902 100644
--- a/src-self-hosted/type.zig
+++ b/src-self-hosted/type.zig
@@ -44,6 +44,7 @@ pub const Type = struct {
Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(comp),
Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(comp),
Id.Promise => @fieldParentPtr(Promise, "base", base).destroy(comp),
+ Id.Vector => @fieldParentPtr(Vector, "base", base).destroy(comp),
}
}
@@ -77,6 +78,7 @@ pub const Type = struct {
Id.ArgTuple => unreachable,
Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context),
Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context),
+ Id.Vector => return @fieldParentPtr(Vector, "base", base).getLlvmType(allocator, llvm_context),
}
}
@@ -103,6 +105,7 @@ pub const Type = struct {
Id.Enum,
Id.Fn,
Id.Promise,
+ Id.Vector,
=> return false,
Id.Struct => @panic("TODO"),
@@ -135,6 +138,7 @@ pub const Type = struct {
Id.Float,
Id.Fn,
Id.Promise,
+ Id.Vector,
=> return true,
Id.Pointer => {
@@ -902,6 +906,18 @@ pub const Type = struct {
}
};
+ pub const Vector = struct {
+ base: Type,
+
+ pub fn destroy(self: *Vector, comp: *Compilation) void {
+ comp.gpa().destroy(self);
+ }
+
+ pub fn getLlvmType(self: *Vector, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ @panic("TODO");
+ }
+ };
+
pub const ComptimeFloat = struct {
base: Type,
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 4b134361a3..f6fe72891d 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -252,6 +252,10 @@ struct ConstArgTuple {
size_t end_index;
};
+struct ConstVector {
+ ConstExprValue *elements;
+};
+
enum ConstValSpecial {
ConstValSpecialRuntime,
ConstValSpecialStatic,
@@ -318,6 +322,7 @@ struct ConstExprValue {
ConstPtrValue x_ptr;
ImportTableEntry *x_import;
ConstArgTuple x_arg_tuple;
+ ConstVector x_vector;
// populated if special == ConstValSpecialRuntime
RuntimeHintErrorUnion rh_error_union;
@@ -1210,6 +1215,12 @@ struct ZigTypePromise {
ZigType *result_type;
};
+struct ZigTypeVector {
+ // The type must be a pointer, integer, or float
+ ZigType *elem_type;
+ uint32_t len;
+};
+
enum ZigTypeId {
ZigTypeIdInvalid,
ZigTypeIdMetaType,
@@ -1236,6 +1247,7 @@ enum ZigTypeId {
ZigTypeIdArgTuple,
ZigTypeIdOpaque,
ZigTypeIdPromise,
+ ZigTypeIdVector,
};
struct ZigType {
@@ -1262,6 +1274,7 @@ struct ZigType {
ZigTypeFn fn;
ZigTypeBoundFn bound_fn;
ZigTypePromise promise;
+ ZigTypeVector vector;
} data;
// use these fields to make sure we don't duplicate type table entries for the same type
@@ -1415,6 +1428,7 @@ enum BuiltinFnId {
BuiltinFnIdEnumToInt,
BuiltinFnIdIntToEnum,
BuiltinFnIdIntType,
+ BuiltinFnIdVectorType,
BuiltinFnIdSetCold,
BuiltinFnIdSetRuntimeSafety,
BuiltinFnIdSetFloatMode,
@@ -1505,6 +1519,10 @@ struct TypeId {
ZigType *err_set_type;
ZigType *payload_type;
} error_union;
+ struct {
+ ZigType *elem_type;
+ uint32_t len;
+ } vector;
} data;
};
@@ -2139,6 +2157,7 @@ enum IrInstructionId {
IrInstructionIdFloatToInt,
IrInstructionIdBoolToInt,
IrInstructionIdIntType,
+ IrInstructionIdVectorType,
IrInstructionIdBoolNot,
IrInstructionIdMemset,
IrInstructionIdMemcpy,
@@ -2807,6 +2826,13 @@ struct IrInstructionIntType {
IrInstruction *bit_count;
};
+struct IrInstructionVectorType {
+ IrInstruction base;
+
+ IrInstruction *len;
+ IrInstruction *elem_type;
+};
+
struct IrInstructionBoolNot {
IrInstruction base;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 194888068c..99378eb7a8 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -250,6 +250,7 @@ AstNode *type_decl_node(ZigType *type_entry) {
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
return nullptr;
}
zig_unreachable();
@@ -311,6 +312,7 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) {
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
return true;
}
zig_unreachable();
@@ -1055,11 +1057,7 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
}
if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);
- if (abi_class == X64CABIClass_MEMORY) {
- return true;
- }
- zig_panic("TODO implement C ABI for x86_64 return types. type '%s'\nSee https://github.com/ziglang/zig/issues/1481",
- buf_ptr(&fn_type_id->return_type->name));
+ return abi_class == X64CABIClass_MEMORY;
} else if (target_is_arm(&g->zig_target)) {
return type_size(g, fn_type_id->return_type) > 16;
}
@@ -1424,6 +1422,7 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) {
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdFn:
+ case ZigTypeIdVector:
return true;
case ZigTypeIdStruct:
return type_entry->data.structure.layout == ContainerLayoutPacked;
@@ -1472,6 +1471,8 @@ static bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
default:
return false;
}
+ case ZigTypeIdVector:
+ return type_allowed_in_extern(g, type_entry->data.vector.elem_type);
case ZigTypeIdFloat:
return true;
case ZigTypeIdArray:
@@ -1625,6 +1626,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
switch (type_requires_comptime(g, type_entry)) {
case ReqCompTimeNo:
break;
@@ -1720,6 +1722,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
switch (type_requires_comptime(g, fn_type_id.return_type)) {
case ReqCompTimeInvalid:
return g->builtin_types.entry_invalid;
@@ -3577,6 +3580,7 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
return type_entry;
}
zig_unreachable();
@@ -3943,6 +3947,7 @@ static bool is_container(ZigType *type_entry) {
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
return false;
}
zig_unreachable();
@@ -4002,6 +4007,7 @@ void resolve_container_type(CodeGen *g, ZigType *type_entry) {
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
zig_unreachable();
}
}
@@ -4451,6 +4457,34 @@ ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
return new_entry;
}
+ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) {
+ TypeId type_id = {};
+ type_id.id = ZigTypeIdVector;
+ type_id.data.vector.len = len;
+ type_id.data.vector.elem_type = elem_type;
+
+ {
+ auto entry = g->type_table.maybe_get(type_id);
+ if (entry)
+ return entry->value;
+ }
+
+ ZigType *entry = new_type_table_entry(ZigTypeIdVector);
+ entry->zero_bits = (len == 0) || !type_has_bits(elem_type);
+ entry->type_ref = entry->zero_bits ? LLVMVoidType() : LLVMVectorType(elem_type->type_ref, len);
+ entry->data.vector.len = len;
+ entry->data.vector.elem_type = elem_type;
+
+ buf_resize(&entry->name, 0);
+ buf_appendf(&entry->name, "@Vector(%u, %s)", len, buf_ptr(&elem_type->name));
+
+ entry->di_type = ZigLLVMDIBuilderCreateVectorType(g->dbuilder, len,
+ LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref), elem_type->di_type);
+
+ g->type_table.put(type_id, entry);
+ return entry;
+}
+
ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) {
return &g->builtin_types.entry_c_int[c_int_type];
}
@@ -4482,6 +4516,7 @@ bool handle_is_ptr(ZigType *type_entry) {
case ZigTypeIdFn:
case ZigTypeIdEnum:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
return false;
case ZigTypeIdArray:
case ZigTypeIdStruct:
@@ -4914,6 +4949,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
return hash_const_val_error_set(const_val);
case ZigTypeIdNamespace:
return hash_ptr(const_val->data.x_import);
+ case ZigTypeIdVector:
+ // TODO better hashing algorithm
+ return 3647867726;
case ZigTypeIdBoundFn:
case ZigTypeIdInvalid:
case ZigTypeIdUnreachable:
@@ -4966,6 +5004,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) {
case ZigTypeIdBool:
case ZigTypeIdUnreachable:
case ZigTypeIdInt:
+ case ZigTypeIdVector:
case ZigTypeIdFloat:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
@@ -5049,6 +5088,7 @@ static bool return_type_is_cacheable(ZigType *return_type) {
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdPointer:
+ case ZigTypeIdVector:
return true;
case ZigTypeIdArray:
@@ -5201,6 +5241,7 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) {
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdInt:
+ case ZigTypeIdVector:
return type_has_bits(type_entry) ? OnePossibleValueNo : OnePossibleValueYes;
case ZigTypeIdPointer:
return type_has_one_possible_value(g, type_entry->data.pointer.child_type);
@@ -5251,6 +5292,7 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) {
case ZigTypeIdErrorSet:
case ZigTypeIdBool:
case ZigTypeIdInt:
+ case ZigTypeIdVector:
case ZigTypeIdFloat:
case ZigTypeIdVoid:
case ZigTypeIdUnreachable:
@@ -5777,7 +5819,7 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
- for (size_t i = 0; i < len; ++i) {
+ for (size_t i = 0; i < len; i += 1) {
if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
return false;
}
@@ -5811,6 +5853,20 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
case ZigTypeIdArgTuple:
return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index &&
a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index;
+ case ZigTypeIdVector: {
+ assert(a->type->data.vector.len == b->type->data.vector.len);
+
+ size_t len = a->type->data.vector.len;
+ ConstExprValue *a_elems = a->data.x_vector.elements;
+ ConstExprValue *b_elems = b->data.x_vector.elements;
+
+ for (size_t i = 0; i < len; i += 1) {
+ if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
+ return false;
+ }
+
+ return true;
+ }
case ZigTypeIdBoundFn:
case ZigTypeIdInvalid:
case ZigTypeIdUnreachable:
@@ -6042,6 +6098,18 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
}
}
zig_unreachable();
+ case ZigTypeIdVector: {
+ buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
+ uint64_t len = type_entry->data.vector.len;
+ for (uint32_t i = 0; i < len; i += 1) {
+ if (i != 0)
+ buf_appendf(buf, ",");
+ ConstExprValue *child_value = &const_val->data.x_vector.elements[i];
+ render_const_value(g, buf, child_value);
+ }
+ buf_appendf(buf, "}");
+ return;
+ }
case ZigTypeIdNull:
{
buf_appendf(buf, "null");
@@ -6200,6 +6268,8 @@ uint32_t type_id_hash(TypeId x) {
case ZigTypeIdInt:
return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) +
(((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557);
+ case ZigTypeIdVector:
+ return hash_ptr(x.data.vector.elem_type) * (x.data.vector.len * 526582681);
}
zig_unreachable();
}
@@ -6248,6 +6318,9 @@ bool type_id_eql(TypeId a, TypeId b) {
case ZigTypeIdInt:
return a.data.integer.is_signed == b.data.integer.is_signed &&
a.data.integer.bit_count == b.data.integer.bit_count;
+ case ZigTypeIdVector:
+ return a.data.vector.elem_type == b.data.vector.elem_type &&
+ a.data.vector.len == b.data.vector.len;
}
zig_unreachable();
}
@@ -6382,6 +6455,7 @@ static const ZigTypeId all_type_ids[] = {
ZigTypeIdArgTuple,
ZigTypeIdOpaque,
ZigTypeIdPromise,
+ ZigTypeIdVector,
};
ZigTypeId type_id_at_index(size_t index) {
@@ -6447,6 +6521,8 @@ size_t type_id_index(ZigType *entry) {
return 22;
case ZigTypeIdPromise:
return 23;
+ case ZigTypeIdVector:
+ return 24;
}
zig_unreachable();
}
@@ -6503,6 +6579,8 @@ const char *type_id_name(ZigTypeId id) {
return "Opaque";
case ZigTypeIdPromise:
return "Promise";
+ case ZigTypeIdVector:
+ return "Vector";
}
zig_unreachable();
}
@@ -6658,6 +6736,7 @@ X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) {
case ZigTypeIdBool:
return X64CABIClass_INTEGER;
case ZigTypeIdFloat:
+ case ZigTypeIdVector:
return X64CABIClass_SSE;
case ZigTypeIdStruct: {
// "If the size of an object is larger than four eightbytes, or it contains unaligned
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 1bac15ebcc..da5265a594 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -20,6 +20,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
uint64_t type_size(CodeGen *g, ZigType *type_entry);
uint64_t type_size_bits(CodeGen *g, ZigType *type_entry);
ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
+ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type);
ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);
ZigType *get_c_int_type(CodeGen *g, CIntType c_int_type);
ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 2f360735dd..e2576f5335 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -1980,7 +1980,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
break;
}
- if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat ||
+ if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || ty->id == ZigTypeIdVector ||
ty->id == ZigTypeIdInt // TODO investigate if we need to change this
) {
switch (fn_walk->id) {
@@ -2660,6 +2660,27 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
} else {
return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
}
+ } else if (type_entry->id == ZigTypeIdVector) {
+ ZigType *elem_type = type_entry->data.vector.elem_type;
+ if (elem_type->id == ZigTypeIdFloat) {
+ ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
+ return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
+ } else if (elem_type->id == ZigTypeIdPointer) {
+ zig_panic("TODO codegen for pointers in vectors");
+ } else if (elem_type->id == ZigTypeIdInt) {
+ bool is_wrapping = (op_id == IrBinOpAddWrap);
+ if (is_wrapping) {
+ return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
+ } else if (want_runtime_safety) {
+ zig_panic("TODO runtime safety for vector integer addition");
+ } else if (elem_type->data.integral.is_signed) {
+ return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
+ } else {
+ return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
+ }
+ } else {
+ zig_unreachable();
+ }
} else {
zig_unreachable();
}
@@ -5211,6 +5232,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdCUndef:
case IrInstructionIdEmbedFile:
case IrInstructionIdIntType:
+ case IrInstructionIdVectorType:
case IrInstructionIdMemberCount:
case IrInstructionIdMemberType:
case IrInstructionIdMemberName:
@@ -5620,6 +5642,8 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
}
case ZigTypeIdArray:
zig_panic("TODO bit pack an array");
+ case ZigTypeIdVector:
+ zig_panic("TODO bit pack a vector");
case ZigTypeIdUnion:
zig_panic("TODO bit pack a union");
case ZigTypeIdStruct:
@@ -5992,6 +6016,14 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
}
}
}
+ case ZigTypeIdVector: {
+ uint32_t len = type_entry->data.vector.len;
+ LLVMValueRef *values = allocate(len);
+ for (uint32_t i = 0; i < len; i += 1) {
+ values[i] = gen_const_val(g, &const_val->data.x_vector.elements[i], "");
+ }
+ return LLVMConstVector(values, len);
+ }
case ZigTypeIdUnion:
{
LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref;
@@ -6927,6 +6959,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int
+ create_builtin_fn(g, BuiltinFnIdVectorType, "Vector", 2);
create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1);
create_builtin_fn(g, BuiltinFnIdSetRuntimeSafety, "setRuntimeSafety", 1);
create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 1);
@@ -7152,6 +7185,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
" ArgTuple: void,\n"
" Opaque: void,\n"
" Promise: Promise,\n"
+ " Vector: Vector,\n"
"\n\n"
" pub const Int = struct {\n"
" is_signed: bool,\n"
@@ -7270,6 +7304,11 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
" child: ?type,\n"
" };\n"
"\n"
+ " pub const Vector = struct {\n"
+ " len: u32,\n"
+ " child: type,\n"
+ " };\n"
+ "\n"
" pub const Definition = struct {\n"
" name: []const u8,\n"
" is_pub: bool,\n"
@@ -7841,6 +7880,9 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e
case ZigTypeIdArray:
prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type);
return;
+ case ZigTypeIdVector:
+ prepend_c_type_to_decl_list(g, gen_h, type_entry->data.vector.elem_type);
+ return;
case ZigTypeIdOptional:
prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type);
return;
@@ -7972,6 +8014,8 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu
buf_appendf(out_buf, "%s", buf_ptr(child_buf));
return;
}
+ case ZigTypeIdVector:
+ zig_panic("TODO implement get_c_type for vector types");
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdFn:
@@ -8137,6 +8181,7 @@ static void gen_h_file(CodeGen *g) {
case ZigTypeIdOptional:
case ZigTypeIdFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
zig_unreachable();
case ZigTypeIdEnum:
if (type_entry->data.enumeration.layout == ContainerLayoutExtern) {
diff --git a/src/ir.cpp b/src/ir.cpp
index 06eb4a47f1..d5152f3c85 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -587,6 +587,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) {
return IrInstructionIdIntType;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorType *) {
+ return IrInstructionIdVectorType;
+}
+
static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) {
return IrInstructionIdBoolNot;
}
@@ -1953,6 +1957,19 @@ static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *s
return &instruction->base;
}
+static IrInstruction *ir_build_vector_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *len,
+ IrInstruction *elem_type)
+{
+ IrInstructionVectorType *instruction = ir_build_instruction(irb, scope, source_node);
+ instruction->len = len;
+ instruction->elem_type = elem_type;
+
+ ir_ref_instruction(len, irb->current_basic_block);
+ ir_ref_instruction(elem_type, irb->current_basic_block);
+
+ return &instruction->base;
+}
+
static IrInstruction *ir_build_bool_not(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionBoolNot *instruction = ir_build_instruction(irb, scope, source_node);
instruction->value = value;
@@ -4230,6 +4247,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, int_type, lval);
}
+ case BuiltinFnIdVectorType:
+ {
+ AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+ IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+ if (arg0_value == irb->codegen->invalid_instruction)
+ return arg0_value;
+
+ AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+ IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
+ if (arg1_value == irb->codegen->invalid_instruction)
+ return arg1_value;
+
+ IrInstruction *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value);
+ return ir_lval_wrap(irb, scope, vector_type, lval);
+ }
case BuiltinFnIdMemcpy:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
@@ -11617,6 +11649,7 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
case ZigTypeIdComptimeInt:
case ZigTypeIdInt:
case ZigTypeIdFloat:
+ case ZigTypeIdVector:
operator_allowed = true;
break;
@@ -12032,6 +12065,48 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b
return result;
}
+static bool ok_float_op(IrBinOp op) {
+ switch (op) {
+ case IrBinOpInvalid:
+ zig_unreachable();
+ case IrBinOpAdd:
+ case IrBinOpSub:
+ case IrBinOpMult:
+ case IrBinOpDivUnspecified:
+ case IrBinOpDivTrunc:
+ case IrBinOpDivFloor:
+ case IrBinOpDivExact:
+ case IrBinOpRemRem:
+ case IrBinOpRemMod:
+ return true;
+
+ case IrBinOpBoolOr:
+ case IrBinOpBoolAnd:
+ case IrBinOpCmpEq:
+ case IrBinOpCmpNotEq:
+ case IrBinOpCmpLessThan:
+ case IrBinOpCmpGreaterThan:
+ case IrBinOpCmpLessOrEq:
+ case IrBinOpCmpGreaterOrEq:
+ case IrBinOpBinOr:
+ case IrBinOpBinXor:
+ case IrBinOpBinAnd:
+ case IrBinOpBitShiftLeftLossy:
+ case IrBinOpBitShiftLeftExact:
+ case IrBinOpBitShiftRightLossy:
+ case IrBinOpBitShiftRightExact:
+ case IrBinOpAddWrap:
+ case IrBinOpSubWrap:
+ case IrBinOpMultWrap:
+ case IrBinOpRemUnspecified:
+ case IrBinOpArrayCat:
+ case IrBinOpArrayMult:
+ case IrBinOpMergeErrorSets:
+ return false;
+ }
+ zig_unreachable();
+}
+
static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *instruction) {
IrInstruction *op1 = instruction->op1->child;
if (type_is_invalid(op1->value.type))
@@ -12169,21 +12244,20 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
op_id = IrBinOpRemRem;
}
+ bool ok = false;
if (is_int) {
- // int
- } else if (is_float &&
- (op_id == IrBinOpAdd ||
- op_id == IrBinOpSub ||
- op_id == IrBinOpMult ||
- op_id == IrBinOpDivUnspecified ||
- op_id == IrBinOpDivTrunc ||
- op_id == IrBinOpDivFloor ||
- op_id == IrBinOpDivExact ||
- op_id == IrBinOpRemRem ||
- op_id == IrBinOpRemMod))
- {
- // float
- } else {
+ ok = true;
+ } else if (is_float && ok_float_op(op_id)) {
+ ok = true;
+ } else if (resolved_type->id == ZigTypeIdVector) {
+ ZigType *elem_type = resolved_type->data.vector.elem_type;
+ if (elem_type->id == ZigTypeIdInt || elem_type->id == ZigTypeIdComptimeInt) {
+ ok = true;
+ } else if ((elem_type->id == ZigTypeIdFloat || elem_type->id == ZigTypeIdComptimeFloat) && ok_float_op(op_id)) {
+ ok = true;
+ }
+ }
+ if (!ok) {
AstNode *source_node = instruction->base.source_node;
ir_add_error_node(ira, source_node,
buf_sprintf("invalid operands to binary expression: '%s' and '%s'",
@@ -12817,6 +12891,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdBool:
+ case ZigTypeIdVector:
break;
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
@@ -12851,6 +12926,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
+ case ZigTypeIdVector:
zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name));
case ZigTypeIdNamespace:
case ZigTypeIdBoundFn:
@@ -14009,6 +14085,7 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
+ case ZigTypeIdVector:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
@@ -15383,37 +15460,7 @@ static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructio
ZigType *type_entry = expr_value->value.type;
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable(); // handled above
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdNamespace:
- case ZigTypeIdBoundFn:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdPointer:
- case ZigTypeIdArray:
- case ZigTypeIdStruct:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- case ZigTypeIdFn:
- case ZigTypeIdArgTuple:
- case ZigTypeIdOpaque:
- case ZigTypeIdPromise:
- return ir_const_type(ira, &typeof_instruction->base, type_entry);
- }
-
- zig_unreachable();
+ return ir_const_type(ira, &typeof_instruction->base, type_entry);
}
static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira,
@@ -15652,6 +15699,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
case ZigTypeIdNamespace:
case ZigTypeIdBoundFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
{
if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
@@ -15772,6 +15820,7 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira,
case ZigTypeIdNamespace:
case ZigTypeIdBoundFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
{
if ((err = ensure_complete_type(ira->codegen, child_type)))
return ira->codegen->invalid_instruction;
@@ -15838,6 +15887,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira,
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
{
uint64_t size_in_bytes = type_size(ira->codegen, type_entry);
return ir_const_unsigned(ira, &size_of_instruction->base, size_in_bytes);
@@ -16307,6 +16357,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira,
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
+ case ZigTypeIdVector:
ir_add_error(ira, &switch_target_instruction->base,
buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name)));
return ira->codegen->invalid_instruction;
@@ -17496,6 +17547,27 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE
break;
}
+ case ZigTypeIdVector: {
+ result = create_const_vals(1);
+ result->special = ConstValSpecialStatic;
+ result->type = ir_type_info_get_type(ira, "Vector", nullptr);
+
+ ConstExprValue *fields = create_const_vals(2);
+ result->data.x_struct.fields = fields;
+
+ // len: usize
+ ensure_field_index(result->type, "len", 0);
+ fields[0].special = ConstValSpecialStatic;
+ fields[0].type = ira->codegen->builtin_types.entry_u32;
+ bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.vector.len);
+ // child: type
+ ensure_field_index(result->type, "child", 1);
+ fields[1].special = ConstValSpecialStatic;
+ fields[1].type = ira->codegen->builtin_types.entry_type;
+ fields[1].data.x_type = type_entry->data.vector.elem_type;
+
+ break;
+ }
case ZigTypeIdOptional:
{
result = create_const_vals(1);
@@ -18671,6 +18743,30 @@ static IrInstruction *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstruct
return ir_const_type(ira, &instruction->base, get_int_type(ira->codegen, is_signed, (uint32_t)bit_count));
}
+static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstructionVectorType *instruction) {
+ uint64_t len;
+ if (!ir_resolve_unsigned(ira, instruction->len->child, ira->codegen->builtin_types.entry_u32, &len))
+ return ira->codegen->invalid_instruction;
+
+ ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child);
+ if (type_is_invalid(elem_type))
+ return ira->codegen->invalid_instruction;
+
+ if (elem_type->id != ZigTypeIdInt &&
+ elem_type->id != ZigTypeIdFloat &&
+ get_codegen_ptr_type(elem_type) == nullptr)
+ {
+ ir_add_error(ira, instruction->elem_type,
+ buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid",
+ buf_ptr(&elem_type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+
+ ZigType *vector_type = get_vector_type(ira->codegen, len, elem_type);
+
+ return ir_const_type(ira, &instruction->base, vector_type);
+}
+
static IrInstruction *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoolNot *instruction) {
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
@@ -19474,6 +19570,7 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
+ case ZigTypeIdVector:
{
uint64_t align_in_bytes = get_abi_alignment(ira->codegen, type_entry);
return ir_const_unsigned(ira, &instruction->base, align_in_bytes);
@@ -20311,6 +20408,15 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
}
}
return;
+ case ZigTypeIdVector: {
+ size_t buf_i = 0;
+ for (uint32_t elem_i = 0; elem_i < val->type->data.vector.len; elem_i += 1) {
+ ConstExprValue *elem = &val->data.x_vector.elements[elem_i];
+ buf_write_value_bytes(codegen, &buf[buf_i], elem);
+ buf_i += type_size(codegen, elem->type);
+ }
+ return;
+ }
case ZigTypeIdStruct:
zig_panic("TODO buf_write_value_bytes struct type");
case ZigTypeIdOptional:
@@ -20387,6 +20493,20 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
}
zig_unreachable();
}
+ case ZigTypeIdVector: {
+ uint64_t elem_size = type_size(codegen, val->type->data.vector.elem_type);
+ uint32_t len = val->type->data.vector.len;
+
+ val->data.x_vector.elements = create_const_vals(len);
+ for (uint32_t i = 0; i < len; i += 1) {
+ ConstExprValue *elem = &val->data.x_vector.elements[i];
+ elem->special = ConstValSpecialStatic;
+ elem->type = val->type->data.vector.elem_type;
+ if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
+ return err;
+ }
+ return ErrorNone;
+ }
case ZigTypeIdEnum:
switch (val->type->data.enumeration.layout) {
case ContainerLayoutAuto:
@@ -21633,6 +21753,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
return ir_analyze_instruction_bool_to_int(ira, (IrInstructionBoolToInt *)instruction);
case IrInstructionIdIntType:
return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction);
+ case IrInstructionIdVectorType:
+ return ir_analyze_instruction_vector_type(ira, (IrInstructionVectorType *)instruction);
case IrInstructionIdBoolNot:
return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction);
case IrInstructionIdMemset:
@@ -21943,6 +22065,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdEmbedFile:
case IrInstructionIdTruncate:
case IrInstructionIdIntType:
+ case IrInstructionIdVectorType:
case IrInstructionIdBoolNot:
case IrInstructionIdSlice:
case IrInstructionIdMemberCount:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index a3ec8e9d35..a1fd450b65 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -719,6 +719,14 @@ static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) {
fprintf(irp->f, ")");
}
+static void ir_print_vector_type(IrPrint *irp, IrInstructionVectorType *instruction) {
+ fprintf(irp->f, "@Vector(");
+ ir_print_other_instruction(irp, instruction->len);
+ fprintf(irp->f, ", ");
+ ir_print_other_instruction(irp, instruction->elem_type);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_bool_not(IrPrint *irp, IrInstructionBoolNot *instruction) {
fprintf(irp->f, "! ");
ir_print_other_instruction(irp, instruction->value);
@@ -1577,6 +1585,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdIntType:
ir_print_int_type(irp, (IrInstructionIntType *)instruction);
break;
+ case IrInstructionIdVectorType:
+ ir_print_vector_type(irp, (IrInstructionVectorType *)instruction);
+ break;
case IrInstructionIdBoolNot:
ir_print_bool_not(irp, (IrInstructionBoolNot *)instruction);
break;
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
index 3c01a0954d..65f6b5abb7 100644
--- a/src/zig_llvm.cpp
+++ b/src/zig_llvm.cpp
@@ -263,6 +263,19 @@ ZigLLVMDIType *ZigLLVMCreateDebugBasicType(ZigLLVMDIBuilder *dibuilder, const ch
return reinterpret_cast(di_type);
}
+struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder,
+ uint64_t Size, uint32_t AlignInBits, struct ZigLLVMDIType *Ty)
+{
+ SmallVector subrange;
+ subrange.push_back(reinterpret_cast(dibuilder)->getOrCreateSubrange(0, Size));
+ DIType *di_type = reinterpret_cast(dibuilder)->createVectorType(
+ Size,
+ AlignInBits,
+ reinterpret_cast(Ty),
+ reinterpret_cast(dibuilder)->getOrCreateArray(subrange));
+ return reinterpret_cast(di_type);
+}
+
ZigLLVMDIType *ZigLLVMCreateDebugArrayType(ZigLLVMDIBuilder *dibuilder, uint64_t size_in_bits,
uint64_t align_in_bits, ZigLLVMDIType *elem_type, int elem_count)
{
diff --git a/src/zig_llvm.h b/src/zig_llvm.h
index 551a4a7448..26dca1198b 100644
--- a/src/zig_llvm.h
+++ b/src/zig_llvm.h
@@ -191,6 +191,9 @@ ZIG_EXTERN_C struct ZigLLVMDISubprogram *ZigLLVMCreateFunction(struct ZigLLVMDIB
unsigned lineno, struct ZigLLVMDIType *fn_di_type, bool is_local_to_unit, bool is_definition,
unsigned scope_line, unsigned flags, bool is_optimized, struct ZigLLVMDISubprogram *decl_subprogram);
+ZIG_EXTERN_C struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder,
+ uint64_t Size, uint32_t AlignInBits, struct ZigLLVMDIType *Ty);
+
ZIG_EXTERN_C void ZigLLVMFnSetSubprogram(LLVMValueRef fn, struct ZigLLVMDISubprogram *subprogram);
ZIG_EXTERN_C void ZigLLVMDIBuilderFinalize(struct ZigLLVMDIBuilder *dibuilder);
diff --git a/std/hash_map.zig b/std/hash_map.zig
index 99237047e0..a63a549814 100644
--- a/std/hash_map.zig
+++ b/std/hash_map.zig
@@ -508,6 +508,7 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type
builtin.TypeId.Optional => @compileError("TODO auto hash for optionals"),
builtin.TypeId.Array => @compileError("TODO auto hash for arrays"),
+ builtin.TypeId.Vector => @compileError("TODO auto hash for vectors"),
builtin.TypeId.Struct => @compileError("TODO auto hash for structs"),
builtin.TypeId.Union => @compileError("TODO auto hash for unions"),
builtin.TypeId.ErrorUnion => @compileError("TODO auto hash for unions"),
@@ -555,5 +556,6 @@ pub fn autoEql(a: var, b: @typeOf(a)) bool {
builtin.TypeId.Struct => @compileError("TODO auto eql for structs"),
builtin.TypeId.Union => @compileError("TODO auto eql for unions"),
builtin.TypeId.ErrorUnion => @compileError("TODO auto eql for unions"),
+ builtin.TypeId.Vector => @compileError("TODO auto eql for vectors"),
}
}
diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig
index 5ad80e06e1..f3bb17b282 100644
--- a/test/stage1/behavior/type_info.zig
+++ b/test/stage1/behavior/type_info.zig
@@ -171,11 +171,11 @@ fn testUnion() void {
assertOrPanic(TypeId(typeinfo_info) == TypeId.Union);
assertOrPanic(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto);
assertOrPanic(typeinfo_info.Union.tag_type.? == TypeId);
- assertOrPanic(typeinfo_info.Union.fields.len == 24);
+ assertOrPanic(typeinfo_info.Union.fields.len == 25);
assertOrPanic(typeinfo_info.Union.fields[4].enum_field != null);
assertOrPanic(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
assertOrPanic(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int));
- assertOrPanic(typeinfo_info.Union.defs.len == 20);
+ assertOrPanic(typeinfo_info.Union.defs.len == 21);
const TestNoTagUnion = union {
Foo: void,
@@ -262,3 +262,15 @@ test "typeInfo with comptime parameter in struct fn def" {
};
comptime var info = @typeInfo(S);
}
+
+test "type info: vectors" {
+ testVector();
+ comptime testVector();
+}
+
+fn testVector() void {
+ const vec_info = @typeInfo(@Vector(4, i32));
+ assertOrPanic(TypeId(vec_info) == TypeId.Vector);
+ assertOrPanic(vec_info.Vector.len == 4);
+ assertOrPanic(vec_info.Vector.child == i32);
+}
--
cgit v1.2.3
From 9b8e23934bc87f1fd6a42cdfdd551212994b6e58 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 1 Feb 2019 17:49:29 -0500
Subject: introduce --single-threaded build option
closes #1764
This adds another boolean to the test matrix; hopefully it does not
inflate the time too much.
std.event.Loop does not work with this option yet. See #1908
---
doc/langref.html.in | 19 +++++++++
src/all_types.hpp | 1 +
src/codegen.cpp | 4 ++
src/main.cpp | 6 +++
std/atomic/queue.zig | 42 ++++++++++++------
std/atomic/stack.zig | 82 ++++++++++++++++++++++++------------
std/event/channel.zig | 3 ++
std/event/future.zig | 3 ++
std/event/group.zig | 3 ++
std/event/lock.zig | 3 ++
std/event/loop.zig | 22 ++++++++++
std/event/net.zig | 3 ++
std/event/rwlock.zig | 3 ++
std/mutex.zig | 66 +++++++++++++++++++++--------
std/os/index.zig | 1 +
std/os/test.zig | 4 ++
std/statically_initialized_mutex.zig | 21 +++++----
test/tests.zig | 43 +++++++++++--------
18 files changed, 246 insertions(+), 83 deletions(-)
(limited to 'src/codegen.cpp')
diff --git a/doc/langref.html.in b/doc/langref.html.in
index e192dbbf76..144c8571c4 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -6714,6 +6714,25 @@ pub fn build(b: *Builder) void {
{#header_close#}
{#see_also|Compile Variables|Zig Build System|Undefined Behavior#}
{#header_close#}
+
+ {#header_open|Single Threaded Builds#}
+ Zig has a compile option --single-threaded which has the following effects:
+
+ - {#link|@atomicLoad#} is emitted as a normal load.
+ - {#link|@atomicRmw#} is emitted as a normal memory load, modify, store.
+ - {#link|@fence#} becomes a no-op.
+ - Variables which have Thread Local Storage instead become globals. TODO thread local variables
+ are not implemented yet.
+ - The overhead of {#link|Coroutines#} becomes equivalent to function call overhead.
+ TODO: please note this will not be implemented until the upcoming Coroutine Rewrite
+ - The {#syntax#}@import("builtin").single_threaded{#endsyntax#} becomes {#syntax#}true{#endsyntax#}
+ and therefore various userland APIs which read this variable become more efficient.
+ For example {#syntax#}std.Mutex{#endsyntax#} becomes
+ an empty data structure and all of its functions become no-ops.
+
+
+ {#header_close#}
+
{#header_open|Undefined Behavior#}
Zig has many instances of undefined behavior. If undefined behavior is
diff --git a/src/all_types.hpp b/src/all_types.hpp
index f6fe72891d..3fc6772b31 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1808,6 +1808,7 @@ struct CodeGen {
bool is_static;
bool strip_debug_symbols;
bool is_test_build;
+ bool is_single_threaded;
bool is_native_target;
bool linker_rdynamic;
bool no_rosegment_workaround;
diff --git a/src/codegen.cpp b/src/codegen.cpp
index e2576f5335..b73fda59d1 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -118,6 +118,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
g->string_literals_table.init(16);
g->type_info_cache.init(32);
g->is_test_build = false;
+ g->is_single_threaded = false;
buf_resize(&g->global_asm, 0);
for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) {
@@ -7377,6 +7378,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
buf_appendf(contents, "pub const endian = %s;\n", endian_str);
}
buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build));
+ buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));
buf_appendf(contents, "pub const os = Os.%s;\n", cur_os);
buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch);
buf_appendf(contents, "pub const environ = Environ.%s;\n", cur_environ);
@@ -7411,6 +7413,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
cache_buf(&cache_hash, compiler_id);
cache_int(&cache_hash, g->build_mode);
cache_bool(&cache_hash, g->is_test_build);
+ cache_bool(&cache_hash, g->is_single_threaded);
cache_int(&cache_hash, g->zig_target.arch.arch);
cache_int(&cache_hash, g->zig_target.arch.sub_arch);
cache_int(&cache_hash, g->zig_target.vendor);
@@ -8329,6 +8332,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
cache_bool(ch, g->is_static);
cache_bool(ch, g->strip_debug_symbols);
cache_bool(ch, g->is_test_build);
+ cache_bool(ch, g->is_single_threaded);
cache_bool(ch, g->is_native_target);
cache_bool(ch, g->linker_rdynamic);
cache_bool(ch, g->no_rosegment_workaround);
diff --git a/src/main.cpp b/src/main.cpp
index fd8e3db2fa..81f49089be 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -59,6 +59,7 @@ static int print_full_usage(const char *arg0) {
" --release-fast build with optimizations on and safety off\n"
" --release-safe build with optimizations on and safety on\n"
" --release-small build with size optimizations on and safety off\n"
+ " --single-threaded source may assume it is only used single-threaded\n"
" --static output will be statically linked\n"
" --strip exclude debug symbols\n"
" --target-arch [name] specify target architecture\n"
@@ -393,6 +394,7 @@ int main(int argc, char **argv) {
bool no_rosegment_workaround = false;
bool system_linker_hack = false;
TargetSubsystem subsystem = TargetSubsystemAuto;
+ bool is_single_threaded = false;
if (argc >= 2 && strcmp(argv[1], "build") == 0) {
Buf zig_exe_path_buf = BUF_INIT;
@@ -550,6 +552,8 @@ int main(int argc, char **argv) {
disable_pic = true;
} else if (strcmp(arg, "--system-linker-hack") == 0) {
system_linker_hack = true;
+ } else if (strcmp(arg, "--single-threaded") == 0) {
+ is_single_threaded = true;
} else if (strcmp(arg, "--test-cmd-bin") == 0) {
test_exec_args.append(nullptr);
} else if (arg[1] == 'L' && arg[2] != 0) {
@@ -816,6 +820,7 @@ int main(int argc, char **argv) {
switch (cmd) {
case CmdBuiltin: {
CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir());
+ g->is_single_threaded = is_single_threaded;
Buf *builtin_source = codegen_generate_builtin_source(g);
if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
@@ -889,6 +894,7 @@ int main(int argc, char **argv) {
codegen_set_out_name(g, buf_out_name);
codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
codegen_set_is_test(g, cmd == CmdTest);
+ g->is_single_threaded = is_single_threaded;
codegen_set_linker_script(g, linker_script);
if (each_lib_rpath)
codegen_set_each_lib_rpath(g, each_lib_rpath);
diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig
index 1aab4c32de..6c61bcc048 100644
--- a/std/atomic/queue.zig
+++ b/std/atomic/queue.zig
@@ -170,20 +170,36 @@ test "std.atomic.Queue" {
.get_count = 0,
};
- var putters: [put_thread_count]*std.os.Thread = undefined;
- for (putters) |*t| {
- t.* = try std.os.spawnThread(&context, startPuts);
- }
- var getters: [put_thread_count]*std.os.Thread = undefined;
- for (getters) |*t| {
- t.* = try std.os.spawnThread(&context, startGets);
- }
+ if (builtin.single_threaded) {
+ {
+ var i: usize = 0;
+ while (i < put_thread_count) : (i += 1) {
+ std.debug.assertOrPanic(startPuts(&context) == 0);
+ }
+ }
+ context.puts_done = 1;
+ {
+ var i: usize = 0;
+ while (i < put_thread_count) : (i += 1) {
+ std.debug.assertOrPanic(startGets(&context) == 0);
+ }
+ }
+ } else {
+ var putters: [put_thread_count]*std.os.Thread = undefined;
+ for (putters) |*t| {
+ t.* = try std.os.spawnThread(&context, startPuts);
+ }
+ var getters: [put_thread_count]*std.os.Thread = undefined;
+ for (getters) |*t| {
+ t.* = try std.os.spawnThread(&context, startGets);
+ }
- for (putters) |t|
- t.wait();
- _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
- for (getters) |t|
- t.wait();
+ for (putters) |t|
+ t.wait();
+ _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
+ for (getters) |t|
+ t.wait();
+ }
if (context.put_sum != context.get_sum) {
std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig
index b69a93733c..1e4981353b 100644
--- a/std/atomic/stack.zig
+++ b/std/atomic/stack.zig
@@ -4,10 +4,13 @@ const AtomicOrder = builtin.AtomicOrder;
/// Many reader, many writer, non-allocating, thread-safe
/// Uses a spinlock to protect push() and pop()
+/// When building in single threaded mode, this is a simple linked list.
pub fn Stack(comptime T: type) type {
return struct {
root: ?*Node,
- lock: u8,
+ lock: @typeOf(lock_init),
+
+ const lock_init = if (builtin.single_threaded) {} else u8(0);
pub const Self = @This();
@@ -19,7 +22,7 @@ pub fn Stack(comptime T: type) type {
pub fn init() Self {
return Self{
.root = null,
- .lock = 0,
+ .lock = lock_init,
};
}
@@ -31,20 +34,31 @@ pub fn Stack(comptime T: type) type {
}
pub fn push(self: *Self, node: *Node) void {
- while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
- defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
-
- node.next = self.root;
- self.root = node;
+ if (builtin.single_threaded) {
+ node.next = self.root;
+ self.root = node;
+ } else {
+ while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
+ defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
+
+ node.next = self.root;
+ self.root = node;
+ }
}
pub fn pop(self: *Self) ?*Node {
- while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
- defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
-
- const root = self.root orelse return null;
- self.root = root.next;
- return root;
+ if (builtin.single_threaded) {
+ const root = self.root orelse return null;
+ self.root = root.next;
+ return root;
+ } else {
+ while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
+ defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
+
+ const root = self.root orelse return null;
+ self.root = root.next;
+ return root;
+ }
}
pub fn isEmpty(self: *Self) bool {
@@ -90,20 +104,36 @@ test "std.atomic.stack" {
.get_count = 0,
};
- var putters: [put_thread_count]*std.os.Thread = undefined;
- for (putters) |*t| {
- t.* = try std.os.spawnThread(&context, startPuts);
- }
- var getters: [put_thread_count]*std.os.Thread = undefined;
- for (getters) |*t| {
- t.* = try std.os.spawnThread(&context, startGets);
- }
+ if (builtin.single_threaded) {
+ {
+ var i: usize = 0;
+ while (i < put_thread_count) : (i += 1) {
+ std.debug.assertOrPanic(startPuts(&context) == 0);
+ }
+ }
+ context.puts_done = 1;
+ {
+ var i: usize = 0;
+ while (i < put_thread_count) : (i += 1) {
+ std.debug.assertOrPanic(startGets(&context) == 0);
+ }
+ }
+ } else {
+ var putters: [put_thread_count]*std.os.Thread = undefined;
+ for (putters) |*t| {
+ t.* = try std.os.spawnThread(&context, startPuts);
+ }
+ var getters: [put_thread_count]*std.os.Thread = undefined;
+ for (getters) |*t| {
+ t.* = try std.os.spawnThread(&context, startGets);
+ }
- for (putters) |t|
- t.wait();
- _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
- for (getters) |t|
- t.wait();
+ for (putters) |t|
+ t.wait();
+ _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
+ for (getters) |t|
+ t.wait();
+ }
if (context.put_sum != context.get_sum) {
std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
diff --git a/std/event/channel.zig b/std/event/channel.zig
index 133ce1c69c..f04ad1604e 100644
--- a/std/event/channel.zig
+++ b/std/event/channel.zig
@@ -319,6 +319,9 @@ pub fn Channel(comptime T: type) type {
}
test "std.event.Channel" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
diff --git a/std/event/future.zig b/std/event/future.zig
index d61768b198..55ed01046d 100644
--- a/std/event/future.zig
+++ b/std/event/future.zig
@@ -84,6 +84,9 @@ pub fn Future(comptime T: type) type {
}
test "std.event.Future" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
diff --git a/std/event/group.zig b/std/event/group.zig
index 9f2687a5b3..0e9c2d9655 100644
--- a/std/event/group.zig
+++ b/std/event/group.zig
@@ -121,6 +121,9 @@ pub fn Group(comptime ReturnType: type) type {
}
test "std.event.Group" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
diff --git a/std/event/lock.zig b/std/event/lock.zig
index 46e0d13468..01978e1909 100644
--- a/std/event/lock.zig
+++ b/std/event/lock.zig
@@ -122,6 +122,9 @@ pub const Lock = struct {
};
test "std.event.Lock" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
diff --git a/std/event/loop.zig b/std/event/loop.zig
index 43965e8293..d5228db604 100644
--- a/std/event/loop.zig
+++ b/std/event/loop.zig
@@ -97,6 +97,7 @@ pub const Loop = struct {
/// TODO copy elision / named return values so that the threads referencing *Loop
/// have the correct pointer value.
pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
+ if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode");
const core_count = try os.cpuCount(allocator);
return self.initInternal(allocator, core_count);
}
@@ -201,6 +202,11 @@ pub const Loop = struct {
self.os_data.fs_thread.wait();
}
+ if (builtin.single_threaded) {
+ assert(extra_thread_count == 0);
+ return;
+ }
+
var extra_thread_index: usize = 0;
errdefer {
// writing 8 bytes to an eventfd cannot fail
@@ -301,6 +307,11 @@ pub const Loop = struct {
self.os_data.fs_thread.wait();
}
+ if (builtin.single_threaded) {
+ assert(extra_thread_count == 0);
+ return;
+ }
+
var extra_thread_index: usize = 0;
errdefer {
_ = os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null) catch unreachable;
@@ -338,6 +349,11 @@ pub const Loop = struct {
self.available_eventfd_resume_nodes.push(eventfd_node);
}
+ if (builtin.single_threaded) {
+ assert(extra_thread_count == 0);
+ return;
+ }
+
var extra_thread_index: usize = 0;
errdefer {
var i: usize = 0;
@@ -845,6 +861,9 @@ pub const Loop = struct {
};
test "std.event.Loop - basic" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
@@ -858,6 +877,9 @@ test "std.event.Loop - basic" {
}
test "std.event.Loop - call" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
diff --git a/std/event/net.zig b/std/event/net.zig
index 6838704084..9dac6aa566 100644
--- a/std/event/net.zig
+++ b/std/event/net.zig
@@ -269,6 +269,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !os.File {
}
test "listen on a port, send bytes, receive bytes" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
if (builtin.os != builtin.Os.linux) {
// TODO build abstractions for other operating systems
return error.SkipZigTest;
diff --git a/std/event/rwlock.zig b/std/event/rwlock.zig
index 5d48ea893e..f272ac71ea 100644
--- a/std/event/rwlock.zig
+++ b/std/event/rwlock.zig
@@ -211,6 +211,9 @@ pub const RwLock = struct {
};
test "std.event.RwLock" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var da = std.heap.DirectAllocator.init();
defer da.deinit();
diff --git a/std/mutex.zig b/std/mutex.zig
index 723581cbef..54173fa38a 100644
--- a/std/mutex.zig
+++ b/std/mutex.zig
@@ -14,7 +14,36 @@ const windows = std.os.windows;
/// If you need static initialization, use std.StaticallyInitializedMutex.
/// The Linux implementation is based on mutex3 from
/// https://www.akkadia.org/drepper/futex.pdf
-pub const Mutex = switch(builtin.os) {
+/// When an application is built in single threaded release mode, all the functions are
+/// no-ops. In single threaded debug mode, there is deadlock detection.
+pub const Mutex = if (builtin.single_threaded)
+ struct {
+ lock: @typeOf(lock_init),
+
+ const lock_init = if (std.debug.runtime_safety) false else {};
+
+ pub const Held = struct {
+ mutex: *Mutex,
+
+ pub fn release(self: Held) void {
+ if (std.debug.runtime_safety) {
+ self.mutex.lock = false;
+ }
+ }
+ };
+ pub fn init() Mutex {
+ return Mutex{ .lock = lock_init };
+ }
+ pub fn deinit(self: *Mutex) void {}
+
+ pub fn acquire(self: *Mutex) Held {
+ if (std.debug.runtime_safety and self.lock) {
+ @panic("deadlock detected");
+ }
+ return Held{ .mutex = self };
+ }
+ }
+else switch (builtin.os) {
builtin.Os.linux => struct {
/// 0: unlocked
/// 1: locked, no waiters
@@ -39,9 +68,7 @@ pub const Mutex = switch(builtin.os) {
};
pub fn init() Mutex {
- return Mutex {
- .lock = 0,
- };
+ return Mutex{ .lock = 0 };
}
pub fn deinit(self: *Mutex) void {}
@@ -60,7 +87,7 @@ pub const Mutex = switch(builtin.os) {
}
c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
}
- return Held { .mutex = self };
+ return Held{ .mutex = self };
}
},
// TODO once https://github.com/ziglang/zig/issues/287 (copy elision) is solved, we can make a
@@ -78,21 +105,19 @@ pub const Mutex = switch(builtin.os) {
mutex: *Mutex,
pub fn release(self: Held) void {
- SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock });
+ SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.lock });
}
};
pub fn init() Mutex {
- return Mutex {
- .lock = SpinLock.init(),
- };
+ return Mutex{ .lock = SpinLock.init() };
}
pub fn deinit(self: *Mutex) void {}
pub fn acquire(self: *Mutex) Held {
_ = self.lock.acquire();
- return Held { .mutex = self };
+ return Held{ .mutex = self };
}
},
};
@@ -122,15 +147,20 @@ test "std.Mutex" {
.data = 0,
};
- const thread_count = 10;
- var threads: [thread_count]*std.os.Thread = undefined;
- for (threads) |*t| {
- t.* = try std.os.spawnThread(&context, worker);
- }
- for (threads) |t|
- t.wait();
+ if (builtin.single_threaded) {
+ worker(&context);
+ std.debug.assertOrPanic(context.data == TestContext.incr_count);
+ } else {
+ const thread_count = 10;
+ var threads: [thread_count]*std.os.Thread = undefined;
+ for (threads) |*t| {
+ t.* = try std.os.spawnThread(&context, worker);
+ }
+ for (threads) |t|
+ t.wait();
- std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
+ std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
+ }
}
fn worker(ctx: *TestContext) void {
diff --git a/std/os/index.zig b/std/os/index.zig
index 78d543583d..75abe3bbde 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -3013,6 +3013,7 @@ pub const SpawnThreadError = error{
/// where T is u8, noreturn, void, or !void
/// caller must call wait on the returned thread
pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread {
+ if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode");
// TODO compile-time call graph analysis to determine stack upper bound
// https://github.com/ziglang/zig/issues/157
const default_stack_size = 8 * 1024 * 1024;
diff --git a/std/os/test.zig b/std/os/test.zig
index 5142920687..f14cf47786 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -40,6 +40,8 @@ fn testThreadIdFn(thread_id: *os.Thread.Id) void {
}
test "std.os.Thread.getCurrentId" {
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var thread_current_id: os.Thread.Id = undefined;
const thread = try os.spawnThread(&thread_current_id, testThreadIdFn);
const thread_id = thread.handle();
@@ -53,6 +55,8 @@ test "std.os.Thread.getCurrentId" {
}
test "spawn threads" {
+ if (builtin.single_threaded) return error.SkipZigTest;
+
var shared_ctx: i32 = 1;
const thread1 = try std.os.spawnThread({}, start1);
diff --git a/std/statically_initialized_mutex.zig b/std/statically_initialized_mutex.zig
index dd875eeaf9..37582d49c1 100644
--- a/std/statically_initialized_mutex.zig
+++ b/std/statically_initialized_mutex.zig
@@ -93,13 +93,18 @@ test "std.StaticallyInitializedMutex" {
.data = 0,
};
- const thread_count = 10;
- var threads: [thread_count]*std.os.Thread = undefined;
- for (threads) |*t| {
- t.* = try std.os.spawnThread(&context, TestContext.worker);
- }
- for (threads) |t|
- t.wait();
+ if (builtin.single_threaded) {
+ TestContext.worker(&context);
+ std.debug.assertOrPanic(context.data == TestContext.incr_count);
+ } else {
+ const thread_count = 10;
+ var threads: [thread_count]*std.os.Thread = undefined;
+ for (threads) |*t| {
+ t.* = try std.os.spawnThread(&context, TestContext.worker);
+ }
+ for (threads) |t|
+ t.wait();
- std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
+ std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
+ }
}
diff --git a/test/tests.zig b/test/tests.zig
index 1ca06b4b34..73d4644d18 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -163,25 +163,32 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons
for (test_targets) |test_target| {
const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch);
for (modes) |mode| {
- for ([]bool{
- false,
- true,
- }) |link_libc| {
- if (link_libc and !is_native) {
- // don't assume we have a cross-compiling libc set up
- continue;
- }
- const these_tests = b.addTest(root_src);
- these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", name, @tagName(test_target.os), @tagName(test_target.arch), @tagName(mode), if (link_libc) "c" else "bare"));
- these_tests.setFilter(test_filter);
- these_tests.setBuildMode(mode);
- if (!is_native) {
- these_tests.setTarget(test_target.arch, test_target.os, test_target.environ);
- }
- if (link_libc) {
- these_tests.linkSystemLibrary("c");
+ for ([]bool{ false, true }) |link_libc| {
+ for ([]bool{ false, true }) |single_threaded| {
+ if (link_libc and !is_native) {
+ // don't assume we have a cross-compiling libc set up
+ continue;
+ }
+ const these_tests = b.addTest(root_src);
+ these_tests.setNamePrefix(b.fmt(
+ "{}-{}-{}-{}-{}-{} ",
+ name,
+ @tagName(test_target.os),
+ @tagName(test_target.arch),
+ @tagName(mode),
+ if (link_libc) "c" else "bare",
+ if (single_threaded) "single" else "multi",
+ ));
+ these_tests.setFilter(test_filter);
+ these_tests.setBuildMode(mode);
+ if (!is_native) {
+ these_tests.setTarget(test_target.arch, test_target.os, test_target.environ);
+ }
+ if (link_libc) {
+ these_tests.linkSystemLibrary("c");
+ }
+ step.dependOn(&these_tests.step);
}
- step.dependOn(&these_tests.step);
}
}
}
--
cgit v1.2.3
From 8c6fa982cd0a02775264b616c37da9907cc603bb Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 4 Feb 2019 20:30:00 -0500
Subject: SIMD: array to vector, vector to array, wrapping int add
also vectors and arrays now use the same ConstExprVal representation
See #903
---
src/all_types.hpp | 20 ++-
src/analyze.cpp | 178 ++++++++++++------------
src/analyze.hpp | 1 +
src/codegen.cpp | 98 +++++++++++---
src/ir.cpp | 291 +++++++++++++++++++++++++---------------
src/ir_print.cpp | 18 +++
test/stage1/behavior.zig | 1 +
test/stage1/behavior/vector.zig | 20 +++
8 files changed, 403 insertions(+), 224 deletions(-)
create mode 100644 test/stage1/behavior/vector.zig
(limited to 'src/codegen.cpp')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 3fc6772b31..c4c9e13cfb 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -252,10 +252,6 @@ struct ConstArgTuple {
size_t end_index;
};
-struct ConstVector {
- ConstExprValue *elements;
-};
-
enum ConstValSpecial {
ConstValSpecialRuntime,
ConstValSpecialStatic,
@@ -322,7 +318,6 @@ struct ConstExprValue {
ConstPtrValue x_ptr;
ImportTableEntry *x_import;
ConstArgTuple x_arg_tuple;
- ConstVector x_vector;
// populated if special == ConstValSpecialRuntime
RuntimeHintErrorUnion rh_error_union;
@@ -2239,6 +2234,8 @@ enum IrInstructionId {
IrInstructionIdToBytes,
IrInstructionIdFromBytes,
IrInstructionIdCheckRuntimeScope,
+ IrInstructionIdVectorToArray,
+ IrInstructionIdArrayToVector,
};
struct IrInstruction {
@@ -3368,6 +3365,19 @@ struct IrInstructionBitReverse {
IrInstruction *op;
};
+struct IrInstructionArrayToVector {
+ IrInstruction base;
+
+ IrInstruction *array;
+};
+
+struct IrInstructionVectorToArray {
+ IrInstruction base;
+
+ IrInstruction *vector;
+ LLVMValueRef tmp_ptr;
+};
+
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 99378eb7a8..ff961a7044 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -4457,7 +4457,15 @@ ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
return new_entry;
}
+bool is_valid_vector_elem_type(ZigType *elem_type) {
+ return elem_type->id == ZigTypeIdInt ||
+ elem_type->id == ZigTypeIdFloat ||
+ get_codegen_ptr_type(elem_type) != nullptr;
+}
+
ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) {
+ assert(is_valid_vector_elem_type(elem_type));
+
TypeId type_id = {};
type_id.id = ZigTypeIdVector;
type_id.data.vector.len = len;
@@ -5749,6 +5757,28 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
zig_unreachable();
}
+static bool const_values_equal_array(CodeGen *g, ConstExprValue *a, ConstExprValue *b, size_t len) {
+ assert(a->data.x_array.special != ConstArraySpecialUndef);
+ assert(b->data.x_array.special != ConstArraySpecialUndef);
+ if (a->data.x_array.special == ConstArraySpecialBuf &&
+ b->data.x_array.special == ConstArraySpecialBuf)
+ {
+ return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
+ }
+ expand_undef_array(g, a);
+ expand_undef_array(g, b);
+
+ ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
+ ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
+
+ for (size_t i = 0; i < len; i += 1) {
+ if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
+ return false;
+ }
+
+ return true;
+}
+
bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
assert(a->type->id == b->type->id);
assert(a->special == ConstValSpecialStatic);
@@ -5803,28 +5833,12 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
case ZigTypeIdPointer:
case ZigTypeIdFn:
return const_values_equal_ptr(a, b);
+ case ZigTypeIdVector:
+ assert(a->type->data.vector.len == b->type->data.vector.len);
+ return const_values_equal_array(g, a, b, a->type->data.vector.len);
case ZigTypeIdArray: {
assert(a->type->data.array.len == b->type->data.array.len);
- assert(a->data.x_array.special != ConstArraySpecialUndef);
- assert(b->data.x_array.special != ConstArraySpecialUndef);
- if (a->data.x_array.special == ConstArraySpecialBuf &&
- b->data.x_array.special == ConstArraySpecialBuf)
- {
- return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
- }
- expand_undef_array(g, a);
- expand_undef_array(g, b);
-
- size_t len = a->type->data.array.len;
- ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
- ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
-
- for (size_t i = 0; i < len; i += 1) {
- if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
- return false;
- }
-
- return true;
+ return const_values_equal_array(g, a, b, a->type->data.array.len);
}
case ZigTypeIdStruct:
for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) {
@@ -5853,20 +5867,6 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
case ZigTypeIdArgTuple:
return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index &&
a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index;
- case ZigTypeIdVector: {
- assert(a->type->data.vector.len == b->type->data.vector.len);
-
- size_t len = a->type->data.vector.len;
- ConstExprValue *a_elems = a->data.x_vector.elements;
- ConstExprValue *b_elems = b->data.x_vector.elements;
-
- for (size_t i = 0; i < len; i += 1) {
- if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
- return false;
- }
-
- return true;
- }
case ZigTypeIdBoundFn:
case ZigTypeIdInvalid:
case ZigTypeIdUnreachable:
@@ -5985,6 +5985,40 @@ static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const
}
}
+static void render_const_val_array(CodeGen *g, Buf *buf, ConstExprValue *const_val, size_t len) {
+ switch (const_val->data.x_array.special) {
+ case ConstArraySpecialUndef:
+ buf_append_str(buf, "undefined");
+ return;
+ case ConstArraySpecialBuf: {
+ Buf *array_buf = const_val->data.x_array.data.s_buf;
+ buf_append_char(buf, '"');
+ for (size_t i = 0; i < buf_len(array_buf); i += 1) {
+ uint8_t c = buf_ptr(array_buf)[i];
+ if (c == '"') {
+ buf_append_str(buf, "\\\"");
+ } else {
+ buf_append_char(buf, c);
+ }
+ }
+ buf_append_char(buf, '"');
+ return;
+ }
+ case ConstArraySpecialNone: {
+ buf_appendf(buf, "%s{", buf_ptr(&const_val->type->name));
+ for (uint64_t i = 0; i < len; i += 1) {
+ if (i != 0)
+ buf_appendf(buf, ",");
+ ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i];
+ render_const_value(g, buf, child_value);
+ }
+ buf_appendf(buf, "}");
+ return;
+ }
+ }
+ zig_unreachable();
+}
+
void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
switch (const_val->special) {
case ConstValSpecialRuntime:
@@ -6065,51 +6099,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
}
case ZigTypeIdPointer:
return render_const_val_ptr(g, buf, const_val, type_entry);
+ case ZigTypeIdVector:
+ return render_const_val_array(g, buf, const_val, type_entry->data.vector.len);
case ZigTypeIdArray:
- switch (const_val->data.x_array.special) {
- case ConstArraySpecialUndef:
- buf_append_str(buf, "undefined");
- return;
- case ConstArraySpecialBuf: {
- Buf *array_buf = const_val->data.x_array.data.s_buf;
- buf_append_char(buf, '"');
- for (size_t i = 0; i < buf_len(array_buf); i += 1) {
- uint8_t c = buf_ptr(array_buf)[i];
- if (c == '"') {
- buf_append_str(buf, "\\\"");
- } else {
- buf_append_char(buf, c);
- }
- }
- buf_append_char(buf, '"');
- return;
- }
- case ConstArraySpecialNone: {
- buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
- uint64_t len = type_entry->data.array.len;
- for (uint64_t i = 0; i < len; i += 1) {
- if (i != 0)
- buf_appendf(buf, ",");
- ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i];
- render_const_value(g, buf, child_value);
- }
- buf_appendf(buf, "}");
- return;
- }
- }
- zig_unreachable();
- case ZigTypeIdVector: {
- buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
- uint64_t len = type_entry->data.vector.len;
- for (uint32_t i = 0; i < len; i += 1) {
- if (i != 0)
- buf_appendf(buf, ",");
- ConstExprValue *child_value = &const_val->data.x_vector.elements[i];
- render_const_value(g, buf, child_value);
- }
- buf_appendf(buf, "}");
- return;
- }
+ return render_const_val_array(g, buf, const_val, type_entry->data.array.len);
case ZigTypeIdNull:
{
buf_appendf(buf, "null");
@@ -6379,7 +6372,17 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
// Canonicalize the array value as ConstArraySpecialNone
void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
- assert(const_val->type->id == ZigTypeIdArray);
+ size_t elem_count;
+ ZigType *elem_type;
+ if (const_val->type->id == ZigTypeIdArray) {
+ elem_count = const_val->type->data.array.len;
+ elem_type = const_val->type->data.array.child_type;
+ } else if (const_val->type->id == ZigTypeIdVector) {
+ elem_count = const_val->type->data.vector.len;
+ elem_type = const_val->type->data.vector.elem_type;
+ } else {
+ zig_unreachable();
+ }
if (const_val->special == ConstValSpecialUndef) {
const_val->special = ConstValSpecialStatic;
const_val->data.x_array.special = ConstArraySpecialUndef;
@@ -6389,18 +6392,14 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
return;
case ConstArraySpecialUndef: {
const_val->data.x_array.special = ConstArraySpecialNone;
- size_t elem_count = const_val->type->data.array.len;
const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
for (size_t i = 0; i < elem_count; i += 1) {
ConstExprValue *element_val = &const_val->data.x_array.data.s_none.elements[i];
- element_val->type = const_val->type->data.array.child_type;
+ element_val->type = elem_type;
init_const_undefined(g, element_val);
- ConstParent *parent = get_const_val_parent(g, element_val);
- if (parent != nullptr) {
- parent->id = ConstParentIdArray;
- parent->data.p_array.array_val = const_val;
- parent->data.p_array.elem_index = i;
- }
+ element_val->parent.id = ConstParentIdArray;
+ element_val->parent.data.p_array.array_val = const_val;
+ element_val->parent.data.p_array.elem_index = i;
}
return;
}
@@ -6411,7 +6410,6 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
g->string_literals_table.maybe_remove(buf);
const_val->data.x_array.special = ConstArraySpecialNone;
- size_t elem_count = const_val->type->data.array.len;
assert(elem_count == buf_len(buf));
const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
for (size_t i = 0; i < elem_count; i += 1) {
@@ -6419,6 +6417,9 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
this_char->special = ConstValSpecialStatic;
this_char->type = g->builtin_types.entry_u8;
bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(buf)[i]);
+ this_char->parent.id = ConstParentIdArray;
+ this_char->parent.data.p_array.array_val = const_val;
+ this_char->parent.data.p_array.elem_index = i;
}
return;
}
@@ -6426,6 +6427,7 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
zig_unreachable();
}
+// Deprecated. Reference the parent field directly.
ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) {
return &value->parent;
}
diff --git a/src/analyze.hpp b/src/analyze.hpp
index da5265a594..f558fa44b0 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -74,6 +74,7 @@ TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag);
bool is_ref(ZigType *type_entry);
bool is_array_ref(ZigType *type_entry);
bool is_container_ref(ZigType *type_entry);
+bool is_valid_vector_elem_type(ZigType *elem_type);
void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
void scan_import(CodeGen *g, ImportTableEntry *import);
void preview_use_decl(CodeGen *g, AstNode *node);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index b73fda59d1..de2222afb7 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -1921,9 +1921,8 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) {
}
static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) {
- assert(alignment > 0);
LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name);
- LLVMSetAlignment(result, alignment);
+ LLVMSetAlignment(result, (alignment == 0) ? get_abi_alignment(g, type_entry) : alignment);
return result;
}
@@ -3246,6 +3245,22 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
}
+static bool value_is_all_undef_array(ConstExprValue *const_val, size_t len) {
+ switch (const_val->data.x_array.special) {
+ case ConstArraySpecialUndef:
+ return true;
+ case ConstArraySpecialBuf:
+ return false;
+ case ConstArraySpecialNone:
+ for (size_t i = 0; i < len; i += 1) {
+ if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i]))
+ return false;
+ }
+ return true;
+ }
+ zig_unreachable();
+}
+
static bool value_is_all_undef(ConstExprValue *const_val) {
switch (const_val->special) {
case ConstValSpecialRuntime:
@@ -3260,19 +3275,9 @@ static bool value_is_all_undef(ConstExprValue *const_val) {
}
return true;
} else if (const_val->type->id == ZigTypeIdArray) {
- switch (const_val->data.x_array.special) {
- case ConstArraySpecialUndef:
- return true;
- case ConstArraySpecialBuf:
- return false;
- case ConstArraySpecialNone:
- for (size_t i = 0; i < const_val->type->data.array.len; i += 1) {
- if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i]))
- return false;
- }
- return true;
- }
- zig_unreachable();
+ return value_is_all_undef_array(const_val, const_val->type->data.array.len);
+ } else if (const_val->type->id == ZigTypeIdVector) {
+ return value_is_all_undef_array(const_val, const_val->type->data.vector.len);
} else {
return false;
}
@@ -5194,6 +5199,32 @@ static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutable *executable,
return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
}
+static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutable *executable,
+ IrInstructionVectorToArray *instruction)
+{
+ ZigType *array_type = instruction->base.value.type;
+ assert(array_type->id == ZigTypeIdArray);
+ assert(handle_is_ptr(array_type));
+ assert(instruction->tmp_ptr);
+ LLVMValueRef vector = ir_llvm_value(g, instruction->vector);
+ LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr,
+ LLVMPointerType(instruction->vector->value.type->type_ref, 0), "");
+ gen_store_untyped(g, vector, casted_ptr, 0, false);
+ return instruction->tmp_ptr;
+}
+
+static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executable,
+ IrInstructionArrayToVector *instruction)
+{
+ ZigType *vector_type = instruction->base.value.type;
+ assert(vector_type->id == ZigTypeIdVector);
+ assert(!handle_is_ptr(vector_type));
+ LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array);
+ LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr,
+ LLVMPointerType(vector_type->type_ref, 0), "");
+ return gen_load_untyped(g, casted_ptr, 0, false, "");
+}
+
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
AstNode *source_node = instruction->source_node;
Scope *scope = instruction->scope;
@@ -5439,6 +5470,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_bswap(g, executable, (IrInstructionBswap *)instruction);
case IrInstructionIdBitReverse:
return ir_render_bit_reverse(g, executable, (IrInstructionBitReverse *)instruction);
+ case IrInstructionIdArrayToVector:
+ return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction);
+ case IrInstructionIdVectorToArray:
+ return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction);
}
zig_unreachable();
}
@@ -6016,14 +6051,32 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), true);
}
}
+ zig_unreachable();
}
case ZigTypeIdVector: {
uint32_t len = type_entry->data.vector.len;
- LLVMValueRef *values = allocate(len);
- for (uint32_t i = 0; i < len; i += 1) {
- values[i] = gen_const_val(g, &const_val->data.x_vector.elements[i], "");
+ switch (const_val->data.x_array.special) {
+ case ConstArraySpecialUndef:
+ return LLVMGetUndef(type_entry->type_ref);
+ case ConstArraySpecialNone: {
+ LLVMValueRef *values = allocate(len);
+ for (uint64_t i = 0; i < len; i += 1) {
+ ConstExprValue *elem_value = &const_val->data.x_array.data.s_none.elements[i];
+ values[i] = gen_const_val(g, elem_value, "");
+ }
+ return LLVMConstVector(values, len);
+ }
+ case ConstArraySpecialBuf: {
+ Buf *buf = const_val->data.x_array.data.s_buf;
+ assert(buf_len(buf) == len);
+ LLVMValueRef *values = allocate(len);
+ for (uint64_t i = 0; i < len; i += 1) {
+ values[i] = LLVMConstInt(g->builtin_types.entry_u8->type_ref, buf_ptr(buf)[i], false);
+ }
+ return LLVMConstVector(values, len);
+ }
}
- return LLVMConstVector(values, len);
+ zig_unreachable();
}
case ZigTypeIdUnion:
{
@@ -6467,6 +6520,7 @@ static void do_code_gen(CodeGen *g) {
IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i);
LLVMValueRef *slot;
ZigType *slot_type = instruction->value.type;
+ uint32_t alignment_bytes = 0;
if (instruction->id == IrInstructionIdCast) {
IrInstructionCast *cast_instruction = (IrInstructionCast *)instruction;
slot = &cast_instruction->tmp_ptr;
@@ -6502,10 +6556,14 @@ static void do_code_gen(CodeGen *g) {
} else if (instruction->id == IrInstructionIdCmpxchgGen) {
IrInstructionCmpxchgGen *cmpxchg_instruction = (IrInstructionCmpxchgGen *)instruction;
slot = &cmpxchg_instruction->tmp_ptr;
+ } else if (instruction->id == IrInstructionIdVectorToArray) {
+ IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction;
+ alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type);
+ slot = &vector_to_array_instruction->tmp_ptr;
} else {
zig_unreachable();
}
- *slot = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type));
+ *slot = build_alloca(g, slot_type, "", alignment_bytes);
}
ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base);
diff --git a/src/ir.cpp b/src/ir.cpp
index 3d3c501df3..3cbbdc8103 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -168,6 +168,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed);
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs);
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
+static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry);
static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) {
assert(get_src_ptr_type(const_val->type) != nullptr);
@@ -899,6 +900,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScop
return IrInstructionIdCheckRuntimeScope;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorToArray *) {
+ return IrInstructionIdVectorToArray;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToVector *) {
+ return IrInstructionIdArrayToVector;
+}
+
template
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate(1);
@@ -2821,6 +2830,34 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope,
return &instruction->base;
}
+static IrInstruction *ir_build_vector_to_array(IrAnalyze *ira, IrInstruction *source_instruction,
+ IrInstruction *vector, ZigType *result_type)
+{
+ IrInstructionVectorToArray *instruction = ir_build_instruction(&ira->new_irb,
+ source_instruction->scope, source_instruction->source_node);
+ instruction->base.value.type = result_type;
+ instruction->vector = vector;
+
+ ir_ref_instruction(vector, ira->new_irb.current_basic_block);
+
+ ir_add_alloca(ira, &instruction->base, result_type);
+
+ return &instruction->base;
+}
+
+static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *source_instruction,
+ IrInstruction *array, ZigType *result_type)
+{
+ IrInstructionArrayToVector *instruction = ir_build_instruction(&ira->new_irb,
+ source_instruction->scope, source_instruction->source_node);
+ instruction->base.value.type = result_type;
+ instruction->array = array;
+
+ ir_ref_instruction(array, ira->new_irb.current_basic_block);
+
+ return &instruction->base;
+}
+
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@@ -8270,6 +8307,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc
bool const_val_is_int = (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt);
bool const_val_is_float = (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat);
+ assert(const_val_is_int || const_val_is_float);
if (other_type->id == ZigTypeIdFloat) {
if (const_val->type->id == ZigTypeIdComptimeInt || const_val->type->id == ZigTypeIdComptimeFloat) {
@@ -10714,6 +10752,32 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
}
}
+static IrInstruction *ir_analyze_array_to_vector(IrAnalyze *ira, IrInstruction *source_instr,
+ IrInstruction *array, ZigType *vector_type)
+{
+ if (instr_is_comptime(array)) {
+ // arrays and vectors have the same ConstExprValue representation
+ IrInstruction *result = ir_const(ira, source_instr, vector_type);
+ copy_const_val(&result->value, &array->value, false);
+ result->value.type = vector_type;
+ return result;
+ }
+ return ir_build_array_to_vector(ira, source_instr, array, vector_type);
+}
+
+static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction *source_instr,
+ IrInstruction *vector, ZigType *array_type)
+{
+ if (instr_is_comptime(vector)) {
+ // arrays and vectors have the same ConstExprValue representation
+ IrInstruction *result = ir_const(ira, source_instr, array_type);
+ copy_const_val(&result->value, &vector->value, false);
+ result->value.type = array_type;
+ return result;
+ }
+ return ir_build_vector_to_array(ira, source_instr, vector, array_type);
+}
+
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
ZigType *wanted_type, IrInstruction *value)
{
@@ -11102,6 +11166,23 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
}
}
+ // cast from @Vector(N, T) to [N]T
+ if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdVector &&
+ wanted_type->data.array.len == actual_type->data.vector.len &&
+ types_match_const_cast_only(ira, wanted_type->data.array.child_type,
+ actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)
+ {
+ return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type);
+ }
+
+ // cast from [N]T to @Vector(N, T)
+ if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdVector &&
+ actual_type->data.array.len == wanted_type->data.vector.len &&
+ types_match_const_cast_only(ira, actual_type->data.array.child_type,
+ wanted_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)
+ {
+ return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type);
+ }
// cast from undefined to anything
if (actual_type->id == ZigTypeIdUndefined) {
@@ -11780,8 +11861,8 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
return result;
}
-static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
- IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val)
+static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry,
+ ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val)
{
bool is_int;
bool is_float;
@@ -11803,10 +11884,10 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod ||
op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ)
{
- return ErrorDivByZero;
+ return ir_add_error(ira, source_instr, buf_sprintf("division by zero"));
}
if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) {
- return ErrorNegativeDenominator;
+ return ir_add_error(ira, source_instr, buf_sprintf("negative denominator"));
}
switch (op_id) {
@@ -11852,7 +11933,7 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
BigInt orig_bigint;
bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint);
if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) {
- return ErrorShiftedOutOneBits;
+ return ir_add_error(ira, source_instr, buf_sprintf("exact shift shifted out 1 bits"));
}
break;
}
@@ -11920,14 +12001,14 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
BigInt remainder;
bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
if (bigint_cmp_zero(&remainder) != CmpEQ) {
- return ErrorExactDivRemainder;
+ return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder"));
}
} else {
float_div_trunc(out_val, op1_val, op2_val);
ConstExprValue remainder;
float_rem(&remainder, op1_val, op2_val);
if (float_cmp_zero(&remainder) != CmpEQ) {
- return ErrorExactDivRemainder;
+ return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder"));
}
}
break;
@@ -11951,13 +12032,51 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count,
type_entry->data.integral.is_signed))
{
- return ErrorOverflow;
+ return ir_add_error(ira, source_instr, buf_sprintf("operation caused overflow"));
}
}
out_val->type = type_entry;
out_val->special = ConstValSpecialStatic;
- return 0;
+ return nullptr;
+}
+
+// This works on operands that have already been checked to be comptime known.
+static IrInstruction *ir_analyze_math_op(IrAnalyze *ira, IrInstruction *source_instr,
+ ZigType *type_entry, ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val)
+{
+ IrInstruction *result_instruction = ir_const(ira, source_instr, type_entry);
+ ConstExprValue *out_val = &result_instruction->value;
+ if (type_entry->id == ZigTypeIdVector) {
+ expand_undef_array(ira->codegen, op1_val);
+ expand_undef_array(ira->codegen, op2_val);
+ out_val->special = ConstValSpecialUndef;
+ expand_undef_array(ira->codegen, out_val);
+ size_t len = type_entry->data.vector.len;
+ ZigType *scalar_type = type_entry->data.vector.elem_type;
+ for (size_t i = 0; i < len; i += 1) {
+ ConstExprValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i];
+ ConstExprValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i];
+ ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i];
+ assert(scalar_op1_val->type == scalar_type);
+ assert(scalar_op2_val->type == scalar_type);
+ assert(scalar_out_val->type == scalar_type);
+ ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type,
+ scalar_op1_val, op_id, scalar_op2_val, scalar_out_val);
+ if (msg != nullptr) {
+ add_error_note(ira->codegen, msg, source_instr->source_node,
+ buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i));
+ return ira->codegen->invalid_instruction;
+ }
+ }
+ out_val->type = type_entry;
+ out_val->special = ConstValSpecialStatic;
+ } else {
+ if (ir_eval_math_op_scalar(ira, source_instr, type_entry, op1_val, op_id, op2_val, out_val) != nullptr) {
+ return ira->codegen->invalid_instruction;
+ }
+ }
+ return ir_implicit_cast(ira, result_instruction, type_entry);
}
static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
@@ -12029,24 +12148,7 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
- IrInstruction *result_instruction = ir_const(ira, &bin_op_instruction->base, op1->value.type);
-
- int err;
- if ((err = ir_eval_math_op(op1->value.type, op1_val, op_id, op2_val, &result_instruction->value))) {
- if (err == ErrorOverflow) {
- ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("operation caused overflow"));
- return ira->codegen->invalid_instruction;
- } else if (err == ErrorShiftedOutOneBits) {
- ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("exact shift shifted out 1 bits"));
- return ira->codegen->invalid_instruction;
- } else {
- zig_unreachable();
- }
- return ira->codegen->invalid_instruction;
- }
-
- ir_num_lit_fits_in_other_type(ira, result_instruction, op1->value.type, false);
- return result_instruction;
+ return ir_analyze_math_op(ira, &bin_op_instruction->base, op1->value.type, op1_val, op_id, op2_val);
} else if (op1->value.type->id == ZigTypeIdComptimeInt) {
ir_add_error(ira, &bin_op_instruction->base,
buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known"));
@@ -12292,30 +12394,7 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
- IrInstruction *result_instruction = ir_const(ira, &instruction->base, resolved_type);
-
- int err;
- if ((err = ir_eval_math_op(resolved_type, op1_val, op_id, op2_val, &result_instruction->value))) {
- if (err == ErrorDivByZero) {
- ir_add_error(ira, &instruction->base, buf_sprintf("division by zero"));
- return ira->codegen->invalid_instruction;
- } else if (err == ErrorOverflow) {
- ir_add_error(ira, &instruction->base, buf_sprintf("operation caused overflow"));
- return ira->codegen->invalid_instruction;
- } else if (err == ErrorExactDivRemainder) {
- ir_add_error(ira, &instruction->base, buf_sprintf("exact division had a remainder"));
- return ira->codegen->invalid_instruction;
- } else if (err == ErrorNegativeDenominator) {
- ir_add_error(ira, &instruction->base, buf_sprintf("negative denominator"));
- return ira->codegen->invalid_instruction;
- } else {
- zig_unreachable();
- }
- return ira->codegen->invalid_instruction;
- }
-
- ir_num_lit_fits_in_other_type(ira, result_instruction, resolved_type, false);
- return result_instruction;
+ return ir_analyze_math_op(ira, &instruction->base, resolved_type, op1_val, op_id, op2_val);
}
IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope,
@@ -18745,10 +18824,7 @@ static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstr
if (type_is_invalid(elem_type))
return ira->codegen->invalid_instruction;
- if (elem_type->id != ZigTypeIdInt &&
- elem_type->id != ZigTypeIdFloat &&
- get_codegen_ptr_type(elem_type) == nullptr)
- {
+ if (!is_valid_vector_elem_type(elem_type)) {
ir_add_error(ira, instruction->elem_type,
buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid",
buf_ptr(&elem_type->name)));
@@ -20345,6 +20421,17 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct
return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value);
}
+static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *val, size_t len) {
+ size_t buf_i = 0;
+ // TODO optimize the buf case
+ expand_undef_array(codegen, val);
+ for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) {
+ ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i];
+ buf_write_value_bytes(codegen, &buf[buf_i], elem);
+ buf_i += type_size(codegen, elem->type);
+ }
+}
+
static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) {
if (val->special == ConstValSpecialUndef)
val->special = ConstValSpecialStatic;
@@ -20390,26 +20477,9 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
zig_unreachable();
}
case ZigTypeIdArray:
- {
- size_t buf_i = 0;
- // TODO optimize the buf case
- expand_undef_array(codegen, val);
- for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) {
- ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i];
- buf_write_value_bytes(codegen, &buf[buf_i], elem);
- buf_i += type_size(codegen, elem->type);
- }
- }
- return;
- case ZigTypeIdVector: {
- size_t buf_i = 0;
- for (uint32_t elem_i = 0; elem_i < val->type->data.vector.len; elem_i += 1) {
- ConstExprValue *elem = &val->data.x_vector.elements[elem_i];
- buf_write_value_bytes(codegen, &buf[buf_i], elem);
- buf_i += type_size(codegen, elem->type);
- }
- return;
- }
+ return buf_write_value_bytes_array(codegen, buf, val, val->type->data.array.len);
+ case ZigTypeIdVector:
+ return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len);
case ZigTypeIdStruct:
zig_panic("TODO buf_write_value_bytes struct type");
case ZigTypeIdOptional:
@@ -20426,6 +20496,31 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
zig_unreachable();
}
+static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf,
+ ConstExprValue *val, ZigType *elem_type, size_t len)
+{
+ Error err;
+ uint64_t elem_size = type_size(codegen, elem_type);
+
+ switch (val->data.x_array.special) {
+ case ConstArraySpecialNone:
+ val->data.x_array.data.s_none.elements = create_const_vals(len);
+ for (size_t i = 0; i < len; i++) {
+ ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i];
+ elem->special = ConstValSpecialStatic;
+ elem->type = elem_type;
+ if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
+ return err;
+ }
+ return ErrorNone;
+ case ConstArraySpecialUndef:
+ zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type");
+ case ConstArraySpecialBuf:
+ zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type");
+ }
+ zig_unreachable();
+}
+
static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val) {
Error err;
assert(val->special == ConstValSpecialStatic);
@@ -20464,42 +20559,12 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn);
return ErrorNone;
}
- case ZigTypeIdArray: {
- uint64_t elem_size = type_size(codegen, val->type->data.array.child_type);
- size_t len = val->type->data.array.len;
-
- switch (val->data.x_array.special) {
- case ConstArraySpecialNone:
- val->data.x_array.data.s_none.elements = create_const_vals(len);
- for (size_t i = 0; i < len; i++) {
- ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i];
- elem->special = ConstValSpecialStatic;
- elem->type = val->type->data.array.child_type;
- if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
- return err;
- }
- return ErrorNone;
- case ConstArraySpecialUndef:
- zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type");
- case ConstArraySpecialBuf:
- zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type");
- }
- zig_unreachable();
- }
- case ZigTypeIdVector: {
- uint64_t elem_size = type_size(codegen, val->type->data.vector.elem_type);
- uint32_t len = val->type->data.vector.len;
-
- val->data.x_vector.elements = create_const_vals(len);
- for (uint32_t i = 0; i < len; i += 1) {
- ConstExprValue *elem = &val->data.x_vector.elements[i];
- elem->special = ConstValSpecialStatic;
- elem->type = val->type->data.vector.elem_type;
- if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
- return err;
- }
- return ErrorNone;
- }
+ case ZigTypeIdArray:
+ return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.array.child_type,
+ val->type->data.array.len);
+ case ZigTypeIdVector:
+ return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.vector.elem_type,
+ val->type->data.vector.len);
case ZigTypeIdEnum:
switch (val->type->data.enumeration.layout) {
case ContainerLayoutAuto:
@@ -21634,6 +21699,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
case IrInstructionIdDeclVarGen:
case IrInstructionIdPtrCastGen:
case IrInstructionIdCmpxchgGen:
+ case IrInstructionIdArrayToVector:
+ case IrInstructionIdVectorToArray:
zig_unreachable();
case IrInstructionIdReturn:
@@ -22129,6 +22196,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdFromBytes:
case IrInstructionIdToBytes:
case IrInstructionIdEnumToInt:
+ case IrInstructionIdVectorToArray:
+ case IrInstructionIdArrayToVector:
return false;
case IrInstructionIdAsm:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index a1fd450b65..e19aa6dda8 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -972,6 +972,18 @@ static void ir_print_check_runtime_scope(IrPrint *irp, IrInstructionCheckRuntime
fprintf(irp->f, ")");
}
+static void ir_print_array_to_vector(IrPrint *irp, IrInstructionArrayToVector *instruction) {
+ fprintf(irp->f, "ArrayToVector(");
+ ir_print_other_instruction(irp, instruction->array);
+ fprintf(irp->f, ")");
+}
+
+static void ir_print_vector_to_array(IrPrint *irp, IrInstructionVectorToArray *instruction) {
+ fprintf(irp->f, "VectorToArray(");
+ ir_print_other_instruction(irp, instruction->vector);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) {
fprintf(irp->f, "inttoerr ");
ir_print_other_instruction(irp, instruction->target);
@@ -1825,6 +1837,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdDeclVarGen:
ir_print_decl_var_gen(irp, (IrInstructionDeclVarGen *)instruction);
break;
+ case IrInstructionIdArrayToVector:
+ ir_print_array_to_vector(irp, (IrInstructionArrayToVector *)instruction);
+ break;
+ case IrInstructionIdVectorToArray:
+ ir_print_vector_to_array(irp, (IrInstructionVectorToArray *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig
index e545a4c418..1fa00b34fd 100644
--- a/test/stage1/behavior.zig
+++ b/test/stage1/behavior.zig
@@ -74,6 +74,7 @@ comptime {
_ = @import("behavior/underscore.zig");
_ = @import("behavior/union.zig");
_ = @import("behavior/var_args.zig");
+ _ = @import("behavior/vector.zig");
_ = @import("behavior/void.zig");
_ = @import("behavior/while.zig");
_ = @import("behavior/widening.zig");
diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig
new file mode 100644
index 0000000000..53c5d01381
--- /dev/null
+++ b/test/stage1/behavior/vector.zig
@@ -0,0 +1,20 @@
+const std = @import("std");
+const assertOrPanic = std.debug.assertOrPanic;
+
+test "implicit array to vector and vector to array" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40};
+ const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4};
+ v +%= x;
+ const result: [4]i32 = v;
+ assertOrPanic(result[0] == 11);
+ assertOrPanic(result[1] == 22);
+ assertOrPanic(result[2] == 33);
+ assertOrPanic(result[3] == 44);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
--
cgit v1.2.3