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.
This commit is contained in:
Andrew Kelley 2019-01-30 23:36:52 -05:00
parent 169a789b34
commit 545064c1d9
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
12 changed files with 419 additions and 54 deletions

View File

@ -1531,6 +1531,29 @@ test "array initialization with function calls" {
{#code_end#}
{#see_also|for|Slices#}
{#header_close#}
{#header_open|Vectors#}
<p>
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#}.
</p>
<p>
TODO talk about C ABI interop
</p>
{#header_open|SIMD#}
<p>
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
</p>
{#header_close#}
{#header_close#}
{#header_open|Pointers#}
<p>
Zig has two kinds of pointers:
@ -6607,6 +6630,17 @@ pub const TypeInfo = union(TypeId) {
expression passed as an argument. The expression is evaluated.
</p>
{#header_close#}
{#header_open|@Vector#}
<pre>{#syntax#}@Vector(comptime len: u32, comptime ElemType: type) type{#endsyntax#}</pre>
<p>
This function returns a vector type for {#link|SIMD#}.
</p>
<p>
{#syntax#}ElemType{#endsyntax#} must be an {#link|integer|Integers#}, a {#link|float|Floats#}, or a
{#link|pointer|Pointers#}.
</p>
{#header_close#}
{#header_close#}

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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<LLVMValueRef>(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) {

View File

@ -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<IrInstructionVectorType>(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<IrInstructionBoolNot>(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:

View File

@ -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;

View File

@ -263,6 +263,19 @@ ZigLLVMDIType *ZigLLVMCreateDebugBasicType(ZigLLVMDIBuilder *dibuilder, const ch
return reinterpret_cast<ZigLLVMDIType*>(di_type);
}
struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder,
uint64_t Size, uint32_t AlignInBits, struct ZigLLVMDIType *Ty)
{
SmallVector<Metadata *, 1> subrange;
subrange.push_back(reinterpret_cast<DIBuilder*>(dibuilder)->getOrCreateSubrange(0, Size));
DIType *di_type = reinterpret_cast<DIBuilder*>(dibuilder)->createVectorType(
Size,
AlignInBits,
reinterpret_cast<DIType*>(Ty),
reinterpret_cast<DIBuilder*>(dibuilder)->getOrCreateArray(subrange));
return reinterpret_cast<ZigLLVMDIType*>(di_type);
}
ZigLLVMDIType *ZigLLVMCreateDebugArrayType(ZigLLVMDIBuilder *dibuilder, uint64_t size_in_bits,
uint64_t align_in_bits, ZigLLVMDIType *elem_type, int elem_count)
{

View File

@ -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);

View File

@ -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"),
}
}

View File

@ -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);
}