Commit 8de14a98a6

Evan Haas <evan@lagerdata.com>
2021-03-08 16:01:19
translate-c: Add support for vector expressions
Includes vector types, __builtin_shufflevector, and __builtin_convertvector
1 parent 38d8aab
lib/std/meta.zig
@@ -1297,3 +1297,35 @@ pub fn globalOption(comptime name: []const u8, comptime T: type) ?T {
         return null;
     return @as(T, @field(root, name));
 }
+
+/// This function is for translate-c and is not intended for general use.
+/// Convert from clang __builtin_shufflevector index to Zig @shuffle index
+/// clang requires __builtin_shufflevector index arguments to be integer constants.
+/// negative values for `this_index` indicate "don't care" so we arbitrarily choose 0
+/// clang enforces that `this_index` is less than the total number of vector elements
+/// See https://ziglang.org/documentation/master/#shuffle
+/// See https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-shufflevector
+pub fn shuffleVectorIndex(comptime this_index: c_int, comptime source_vector_len: usize) i32 {
+    if (this_index <= 0) return 0;
+
+    const positive_index = @intCast(usize, this_index);
+    if (positive_index < source_vector_len) return @intCast(i32, this_index);
+    const b_index = positive_index - source_vector_len;
+    return ~@intCast(i32, b_index);
+}
+
+test "shuffleVectorIndex" {
+    const vector_len: usize = 4;
+
+    testing.expect(shuffleVectorIndex(-1, vector_len) == 0);
+
+    testing.expect(shuffleVectorIndex(0, vector_len) == 0);
+    testing.expect(shuffleVectorIndex(1, vector_len) == 1);
+    testing.expect(shuffleVectorIndex(2, vector_len) == 2);
+    testing.expect(shuffleVectorIndex(3, vector_len) == 3);
+
+    testing.expect(shuffleVectorIndex(4, vector_len) == -1);
+    testing.expect(shuffleVectorIndex(5, vector_len) == -2);
+    testing.expect(shuffleVectorIndex(6, vector_len) == -3);
+    testing.expect(shuffleVectorIndex(7, vector_len) == -4);
+}
\ No newline at end of file
src/translate_c/ast.zig
@@ -66,6 +66,7 @@ pub const Node = extern union {
         @"enum",
         @"struct",
         @"union",
+        @"comptime",
         array_init,
         tuple,
         container_init,
@@ -154,6 +155,8 @@ pub const Node = extern union {
         div_exact,
         /// @byteOffsetOf(lhs, rhs)
         byte_offset_of,
+        /// @shuffle(type, a, b, mask)
+        shuffle,
 
         negate,
         negate_wrap,
@@ -172,6 +175,7 @@ pub const Node = extern union {
         sizeof,
         alignof,
         typeof,
+        typeinfo,
         type,
 
         optional_type,
@@ -182,6 +186,10 @@ pub const Node = extern union {
 
         /// @import("std").meta.sizeof(operand)
         std_meta_sizeof,
+        /// @import("std").meta.shuffleVectorIndex(lhs, rhs)
+        std_meta_shuffle_vector_index,
+        /// @import("std").meta.Vector(lhs, rhs)
+        std_meta_vector,
         /// @import("std").mem.zeroes(operand)
         std_mem_zeroes,
         /// @import("std").mem.zeroInit(lhs, rhs)
@@ -233,6 +241,7 @@ pub const Node = extern union {
 
                 .std_mem_zeroes,
                 .@"return",
+                .@"comptime",
                 .discard,
                 .std_math_Log2Int,
                 .negate,
@@ -255,6 +264,7 @@ pub const Node = extern union {
                 .sizeof,
                 .alignof,
                 .typeof,
+                .typeinfo,
                 => Payload.UnOp,
 
                 .add,
@@ -308,6 +318,8 @@ pub const Node = extern union {
                 .align_cast,
                 .array_access,
                 .std_mem_zeroinit,
+                .std_meta_shuffle_vector_index,
+                .std_meta_vector,
                 .ptr_cast,
                 .div_exact,
                 .byte_offset_of,
@@ -346,6 +358,7 @@ pub const Node = extern union {
                 .pub_inline_fn => Payload.PubInlineFn,
                 .field_access => Payload.FieldAccess,
                 .string_slice => Payload.StringSlice,
+                .shuffle => Payload.Shuffle,
             };
         }
 
@@ -678,6 +691,16 @@ pub const Payload = struct {
             end: usize,
         },
     };
+
+    pub const Shuffle = struct {
+        base: Payload,
+        data: struct {
+            element_type: Node,
+            a: Node,
+            b: Node,
+            mask_vector: Node,
+        },
+    };
 };
 
 /// Converts the nodes into a Zig ast.
@@ -868,6 +891,16 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
             const import_node = try renderStdImport(c, "mem", "zeroInit");
             return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
         },
+        .std_meta_shuffle_vector_index => {
+            const payload = node.castTag(.std_meta_shuffle_vector_index).?.data;
+            const import_node = try renderStdImport(c, "meta", "shuffleVectorIndex");
+            return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
+        },
+        .std_meta_vector => {
+            const payload = node.castTag(.std_meta_vector).?.data;
+            const import_node = try renderStdImport(c, "meta", "Vector");
+            return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
+        },
         .call => {
             const payload = node.castTag(.call).?.data;
             const lhs = try renderNode(c, payload.lhs);
@@ -964,6 +997,17 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
                 },
             });
         },
+        .@"comptime" => {
+            const payload = node.castTag(.@"comptime").?.data;
+            return c.addNode(.{
+                .tag = .@"comptime",
+                .main_token = try c.addToken(.keyword_comptime, "comptime"),
+                .data = .{
+                    .lhs = try renderNode(c, payload),
+                    .rhs = undefined,
+                },
+            });
+        },
         .type => {
             const payload = node.castTag(.type).?.data;
             return c.addNode(.{
@@ -1217,6 +1261,15 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
             const payload = node.castTag(.sizeof).?.data;
             return renderBuiltinCall(c, "@sizeOf", &.{payload});
         },
+        .shuffle => {
+            const payload = node.castTag(.shuffle).?.data;
+            return renderBuiltinCall(c, "@shuffle", &.{
+                payload.element_type,
+                payload.a,
+                payload.b,
+                payload.mask_vector,
+            });
+        },
         .alignof => {
             const payload = node.castTag(.alignof).?.data;
             return renderBuiltinCall(c, "@alignOf", &.{payload});
@@ -1225,6 +1278,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
             const payload = node.castTag(.typeof).?.data;
             return renderBuiltinCall(c, "@TypeOf", &.{payload});
         },
+        .typeinfo => {
+            const payload = node.castTag(.typeinfo).?.data;
+            return renderBuiltinCall(c, "@typeInfo", &.{payload});
+        },
         .negate => return renderPrefixOp(c, node, .negation, .minus, "-"),
         .negate_wrap => return renderPrefixOp(c, node, .negation_wrap, .minus_percent, "-%"),
         .bit_not => return renderPrefixOp(c, node, .bit_not, .tilde, "~"),
@@ -2085,9 +2142,12 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
         .sizeof,
         .alignof,
         .typeof,
+        .typeinfo,
         .std_meta_sizeof,
         .std_meta_cast,
         .std_meta_promoteIntLiteral,
+        .std_meta_vector,
+        .std_meta_shuffle_vector_index,
         .std_mem_zeroinit,
         .integer_literal,
         .float_literal,
@@ -2118,6 +2178,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
         .bool_to_int,
         .div_exact,
         .byte_offset_of,
+        .shuffle,
         => {
             // no grouping needed
             return renderNode(c, node);
@@ -2185,6 +2246,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
         .discard,
         .@"continue",
         .@"return",
+        .@"comptime",
         .usingnamespace_builtins,
         .while_true,
         .if_not_break,
@@ -2327,6 +2389,8 @@ fn renderBuiltinCall(c: *Context, builtin: []const u8, args: []const Node) !Node
     _ = try c.addToken(.l_paren, "(");
     var arg_1: NodeIndex = 0;
     var arg_2: NodeIndex = 0;
+    var arg_3: NodeIndex = 0;
+    var arg_4: NodeIndex = 0;
     switch (args.len) {
         0 => {},
         1 => {
@@ -2337,18 +2401,41 @@ fn renderBuiltinCall(c: *Context, builtin: []const u8, args: []const Node) !Node
             _ = try c.addToken(.comma, ",");
             arg_2 = try renderNode(c, args[1]);
         },
+        4 => {
+            arg_1 = try renderNode(c, args[0]);
+            _ = try c.addToken(.comma, ",");
+            arg_2 = try renderNode(c, args[1]);
+            _ = try c.addToken(.comma, ",");
+            arg_3 = try renderNode(c, args[2]);
+            _ = try c.addToken(.comma, ",");
+            arg_4 = try renderNode(c, args[3]);
+        },
         else => unreachable, // expand this function as needed.
     }
 
     _ = try c.addToken(.r_paren, ")");
-    return c.addNode(.{
-        .tag = .builtin_call_two,
-        .main_token = builtin_tok,
-        .data = .{
-            .lhs = arg_1,
-            .rhs = arg_2,
-        },
-    });
+    if (args.len <= 2) {
+        return c.addNode(.{
+            .tag = .builtin_call_two,
+            .main_token = builtin_tok,
+            .data = .{
+                .lhs = arg_1,
+                .rhs = arg_2,
+            },
+        });
+    } else {
+        std.debug.assert(args.len == 4);
+
+        const params = try c.listToSpan(&.{ arg_1, arg_2, arg_3, arg_4 });
+        return c.addNode(.{
+            .tag = .builtin_call,
+            .main_token = builtin_tok,
+            .data = .{
+                .lhs = params.start,
+                .rhs = params.end,
+            },
+        });
+    }
 }
 
 fn renderVar(c: *Context, node: Node) !NodeIndex {
src/clang.zig
@@ -300,6 +300,14 @@ pub const ConstantExpr = opaque {};
 
 pub const ContinueStmt = opaque {};
 
+pub const ConvertVectorExpr = opaque {
+    pub const getSrcExpr = ZigClangConvertVectorExpr_getSrcExpr;
+    extern fn ZigClangConvertVectorExpr_getSrcExpr(*const ConvertVectorExpr) *const Expr;
+
+    pub const getTypeSourceInfo_getType = ZigClangConvertVectorExpr_getTypeSourceInfo_getType;
+    extern fn ZigClangConvertVectorExpr_getTypeSourceInfo_getType(*const ConvertVectorExpr) QualType;
+};
+
 pub const DecayedType = opaque {
     pub const getDecayedType = ZigClangDecayedType_getDecayedType;
     extern fn ZigClangDecayedType_getDecayedType(*const DecayedType) QualType;
@@ -748,6 +756,14 @@ pub const ReturnStmt = opaque {
     extern fn ZigClangReturnStmt_getRetValue(*const ReturnStmt) ?*const Expr;
 };
 
+pub const ShuffleVectorExpr = opaque {
+    pub const getNumSubExprs = ZigClangShuffleVectorExpr_getNumSubExprs;
+    extern fn ZigClangShuffleVectorExpr_getNumSubExprs(*const ShuffleVectorExpr) c_uint;
+
+    pub const getExpr = ZigClangShuffleVectorExpr_getExpr;
+    extern fn ZigClangShuffleVectorExpr_getExpr(*const ShuffleVectorExpr, c_uint) *const Expr;
+};
+
 pub const SourceManager = opaque {
     pub const getSpellingLoc = ZigClangSourceManager_getSpellingLoc;
     extern fn ZigClangSourceManager_getSpellingLoc(*const SourceManager, Loc: SourceLocation) SourceLocation;
@@ -837,6 +853,9 @@ pub const Type = opaque {
     pub const isRecordType = ZigClangType_isRecordType;
     extern fn ZigClangType_isRecordType(*const Type) bool;
 
+    pub const isVectorType = ZigClangType_isVectorType;
+    extern fn ZigClangType_isVectorType(*const Type) bool;
+
     pub const isIncompleteOrZeroLengthArrayType = ZigClangType_isIncompleteOrZeroLengthArrayType;
     extern fn ZigClangType_isIncompleteOrZeroLengthArrayType(*const Type, *const ASTContext) bool;
 
@@ -937,6 +956,14 @@ pub const VarDecl = opaque {
     extern fn ZigClangVarDecl_getTypeSourceInfo_getType(*const VarDecl) QualType;
 };
 
+pub const VectorType = opaque {
+    pub const getElementType = ZigClangVectorType_getElementType;
+    extern fn ZigClangVectorType_getElementType(*const VectorType) QualType;
+
+    pub const getNumElements = ZigClangVectorType_getNumElements;
+    extern fn ZigClangVectorType_getNumElements(*const VectorType) c_uint;
+};
+
 pub const WhileStmt = opaque {
     pub const getCond = ZigClangWhileStmt_getCond;
     extern fn ZigClangWhileStmt_getCond(*const WhileStmt) *const Expr;
src/translate_c.zig
@@ -1123,6 +1123,16 @@ fn transStmt(
             const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt);
             return transExpr(c, scope, gen_sel.getResultExpr(), result_used);
         },
+        .ConvertVectorExprClass => {
+            const conv_vec = @ptrCast(*const clang.ConvertVectorExpr, stmt);
+            const conv_vec_node = try transConvertVectorExpr(c, scope, stmt.getBeginLoc(), conv_vec);
+            return maybeSuppressResult(c, scope, result_used, conv_vec_node);
+        },
+        .ShuffleVectorExprClass => {
+            const shuffle_vec_expr = @ptrCast(*const clang.ShuffleVectorExpr, stmt);
+            const shuffle_vec_node = try transShuffleVectorExpr(c, scope, shuffle_vec_expr);
+            return maybeSuppressResult(c, scope, result_used, shuffle_vec_node);
+        },
         // When adding new cases here, see comment for maybeBlockify()
         else => {
             return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
@@ -1130,6 +1140,128 @@ fn transStmt(
     }
 }
 
+/// See https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-convertvector
+fn transConvertVectorExpr(
+    c: *Context,
+    scope: *Scope,
+    source_loc: clang.SourceLocation,
+    expr: *const clang.ConvertVectorExpr,
+) TransError!Node {
+    const base_stmt = @ptrCast(*const clang.Stmt, expr);
+
+    var block_scope = try Scope.Block.init(c, scope, true);
+    defer block_scope.deinit();
+
+    const src_expr = expr.getSrcExpr();
+    const src_type = qualTypeCanon(src_expr.getType());
+    const src_vector_ty = @ptrCast(*const clang.VectorType, src_type);
+    const src_element_qt = src_vector_ty.getElementType();
+    const src_element_type_node = try transQualType(c, &block_scope.base, src_element_qt, base_stmt.getBeginLoc());
+
+    const src_expr_node = try transExpr(c, &block_scope.base, src_expr, .used);
+
+    const dst_qt = expr.getTypeSourceInfo_getType();
+    const dst_type_node = try transQualType(c, &block_scope.base, dst_qt, base_stmt.getBeginLoc());
+    const dst_vector_ty = @ptrCast(*const clang.VectorType, qualTypeCanon(dst_qt));
+    const num_elements = dst_vector_ty.getNumElements();
+    const dst_element_qt = dst_vector_ty.getElementType();
+
+    // workaround for https://github.com/ziglang/zig/issues/8322
+    // we store the casted results into temp variables and use those
+    // to initialize the vector. Eventually we can just directly
+    // construct the init_list from casted source members
+    var i: usize = 0;
+    while (i < num_elements) : (i += 1) {
+        const mangled_name = try block_scope.makeMangledName(c, "tmp");
+        const value = try Tag.array_access.create(c.arena, .{
+            .lhs = src_expr_node,
+            .rhs = try transCreateNodeNumber(c, i, .int),
+        });
+        const tmp_decl_node = try Tag.var_simple.create(c.arena, .{
+            .name = mangled_name,
+            .init = try transCCast(c, &block_scope.base, base_stmt.getBeginLoc(), dst_element_qt, src_element_qt, value),
+        });
+        try block_scope.statements.append(tmp_decl_node);
+    }
+
+    const init_list = try c.arena.alloc(Node, num_elements);
+    for (init_list) |*init, init_index| {
+        const tmp_decl = block_scope.statements.items[init_index];
+        const name = tmp_decl.castTag(.var_simple).?.data.name;
+        init.* = try Tag.identifier.create(c.arena, name);
+    }
+
+    const vec_init = try Tag.array_init.create(c.arena, .{
+        .cond = dst_type_node,
+        .cases = init_list,
+    });
+
+    const break_node = try Tag.break_val.create(c.arena, .{
+        .label = block_scope.label,
+        .val = vec_init,
+    });
+    try block_scope.statements.append(break_node);
+    return block_scope.complete(c);
+}
+
+fn makeShuffleMask(c: *Context, scope: *Scope, expr: *const clang.ShuffleVectorExpr, vector_len: Node) TransError!Node {
+    const num_subexprs = expr.getNumSubExprs();
+    assert(num_subexprs >= 3); // two source vectors + at least 1 index expression
+    const mask_len = num_subexprs - 2;
+
+    const mask_type = try Tag.std_meta_vector.create(c.arena, .{
+        .lhs = try transCreateNodeNumber(c, mask_len, .int),
+        .rhs = try Tag.type.create(c.arena, "i32"),
+    });
+
+    const init_list = try c.arena.alloc(Node, mask_len);
+
+    for (init_list) |*init, i| {
+        const index_expr = try transExprCoercing(c, scope, expr.getExpr(@intCast(c_uint, i + 2)), .used);
+        const converted_index = try Tag.std_meta_shuffle_vector_index.create(c.arena, .{ .lhs = index_expr, .rhs = vector_len });
+        init.* = converted_index;
+    }
+
+    const mask_init = try Tag.array_init.create(c.arena, .{
+        .cond = mask_type,
+        .cases = init_list,
+    });
+    return Tag.@"comptime".create(c.arena, mask_init);
+}
+
+/// @typeInfo(@TypeOf(vec_node)).Vector.<field>
+fn vectorTypeInfo(arena: *mem.Allocator, vec_node: Node, field: []const u8) TransError!Node {
+    const typeof_call = try Tag.typeof.create(arena, vec_node);
+    const typeinfo_call = try Tag.typeinfo.create(arena, typeof_call);
+    const vector_type_info = try Tag.field_access.create(arena, .{ .lhs = typeinfo_call, .field_name = "Vector" });
+    return Tag.field_access.create(arena, .{ .lhs = vector_type_info, .field_name = field });
+}
+
+fn transShuffleVectorExpr(
+    c: *Context,
+    scope: *Scope,
+    expr: *const clang.ShuffleVectorExpr,
+) TransError!Node {
+    const base_expr = @ptrCast(*const clang.Expr, expr);
+    const num_subexprs = expr.getNumSubExprs();
+    if (num_subexprs < 3) return fail(c, error.UnsupportedTranslation, base_expr.getBeginLoc(), "ShuffleVector needs at least 1 index", .{});
+
+    const a = try transExpr(c, scope, expr.getExpr(0), .used);
+    const b = try transExpr(c, scope, expr.getExpr(1), .used);
+
+    // clang requires first two arguments to __builtin_shufflevector to be same type
+    const vector_child_type = try vectorTypeInfo(c.arena, a, "child");
+    const vector_len = try vectorTypeInfo(c.arena, a, "len");
+    const shuffle_mask = try makeShuffleMask(c, scope, expr, vector_len);
+
+    return Tag.shuffle.create(c.arena, .{
+        .element_type = vector_child_type,
+        .a = a,
+        .b = b,
+        .mask_vector = shuffle_mask,
+    });
+}
+
 /// Translate a "simple" offsetof expression containing exactly one component,
 /// when that component is of kind .Field - e.g. offsetof(mytype, myfield)
 fn transSimpleOffsetOfExpr(
@@ -1935,6 +2067,10 @@ fn cIsEnum(qt: clang.QualType) bool {
     return qt.getCanonicalType().getTypeClass() == .Enum;
 }
 
+fn cIsVector(qt: clang.QualType) bool {
+    return qt.getCanonicalType().getTypeClass() == .Vector;
+}
+
 /// Get the underlying int type of an enum. The C compiler chooses a signed int
 /// type that is large enough to hold all of the enum's values. It is not required
 /// to be the smallest possible type that can hold all the values.
@@ -1991,6 +2127,11 @@ fn transCCast(
         // @bitCast(dest_type, intermediate_value)
         return Tag.bit_cast.create(c.arena, .{ .lhs = dst_node, .rhs = src_int_expr });
     }
+    if (cIsVector(src_type) or cIsVector(dst_type)) {
+        // C cast where at least 1 operand is a vector requires them to be same size
+        // @bitCast(dest_type, val)
+        return Tag.bit_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
+    }
     if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) {
         // @intCast(dest_type, @ptrToInt(val))
         const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr);
@@ -2209,6 +2350,63 @@ fn transInitListExprArray(
     }
 }
 
+fn transInitListExprVector(
+    c: *Context,
+    scope: *Scope,
+    loc: clang.SourceLocation,
+    expr: *const clang.InitListExpr,
+    ty: *const clang.Type,
+) TransError!Node {
+
+    const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr));
+    const vector_type = try transQualType(c, scope, qt, loc);
+    const init_count = expr.getNumInits();
+
+    if (init_count == 0) {
+        return Tag.container_init.create(c.arena, .{
+            .lhs = vector_type,
+            .inits = try c.arena.alloc(ast.Payload.ContainerInit.Initializer, 0),
+        });
+    }
+
+    var block_scope = try Scope.Block.init(c, scope, true);
+    defer block_scope.deinit();
+
+    // workaround for https://github.com/ziglang/zig/issues/8322
+    // we store the initializers in temp variables and use those
+    // to initialize the vector. Eventually we can just directly
+    // construct the init_list from casted source members
+    var i: usize = 0;
+    while (i < init_count) : (i += 1) {
+        const mangled_name = try block_scope.makeMangledName(c, "tmp");
+        const init_expr = expr.getInit(@intCast(c_uint, i));
+        const tmp_decl_node = try Tag.var_simple.create(c.arena, .{
+            .name = mangled_name,
+            .init = try transExpr(c, &block_scope.base, init_expr, .used),
+        });
+        try block_scope.statements.append(tmp_decl_node);
+    }
+
+    const init_list = try c.arena.alloc(Node, init_count);
+    for (init_list) |*init, init_index| {
+        const tmp_decl = block_scope.statements.items[init_index];
+        const name = tmp_decl.castTag(.var_simple).?.data.name;
+        init.* = try Tag.identifier.create(c.arena, name);
+    }
+
+    const array_init = try Tag.array_init.create(c.arena, .{
+        .cond = vector_type,
+        .cases = init_list,
+    });
+    const break_node = try Tag.break_val.create(c.arena, .{
+        .label = block_scope.label,
+        .val = array_init,
+    });
+    try block_scope.statements.append(break_node);
+
+    return block_scope.complete(c);
+}
+
 fn transInitListExpr(
     c: *Context,
     scope: *Scope,
@@ -2235,6 +2433,14 @@ fn transInitListExpr(
             expr,
             qual_type,
         ));
+    } else if (qual_type.isVectorType()) {
+        return maybeSuppressResult(c, scope, used, try transInitListExprVector(
+            c,
+            scope,
+            source_loc,
+            expr,
+            qual_type,
+        ));
     } else {
         const type_name = c.str(qual_type.getTypeClassName());
         return fail(c, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name});
@@ -4085,6 +4291,15 @@ fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clan
             };
             return Tag.typeof.create(c.arena, underlying_expr);
         },
+        .Vector => {
+            const vector_ty = @ptrCast(*const clang.VectorType, ty);
+            const num_elements = vector_ty.getNumElements();
+            const element_qt = vector_ty.getElementType();
+            return Tag.std_meta_vector.create(c.arena, .{
+                .lhs = try transCreateNodeNumber(c, num_elements, .int),
+                .rhs = try transQualType(c, scope, element_qt, source_loc),
+            });
+        },
         else => {
             const type_name = c.str(ty.getTypeClassName());
             return fail(c, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name});
src/zig_clang.cpp
@@ -2046,6 +2046,11 @@ bool ZigClangType_isRecordType(const ZigClangType *self) {
     return casted->isRecordType();
 }
 
+bool ZigClangType_isVectorType(const ZigClangType *self) {
+    auto casted = reinterpret_cast<const clang::Type *>(self);
+    return casted->isVectorType();
+}
+
 bool ZigClangType_isIncompleteOrZeroLengthArrayType(const ZigClangQualType *self,
         const struct ZigClangASTContext *ctx)
 {
@@ -2738,6 +2743,16 @@ struct ZigClangQualType ZigClangBinaryOperator_getType(const struct ZigClangBina
     return bitcast(casted->getType());
 }
 
+const struct ZigClangExpr *ZigClangConvertVectorExpr_getSrcExpr(const struct ZigClangConvertVectorExpr *self) {
+    auto casted = reinterpret_cast<const clang::ConvertVectorExpr *>(self);
+    return reinterpret_cast<const struct ZigClangExpr *>(casted->getSrcExpr());
+}
+
+struct ZigClangQualType ZigClangConvertVectorExpr_getTypeSourceInfo_getType(const struct ZigClangConvertVectorExpr *self) {
+    auto casted = reinterpret_cast<const clang::ConvertVectorExpr *>(self);
+    return bitcast(casted->getTypeSourceInfo()->getType());
+}
+
 struct ZigClangQualType ZigClangDecayedType_getDecayedType(const struct ZigClangDecayedType *self) {
     auto casted = reinterpret_cast<const clang::DecayedType *>(self);
     return bitcast(casted->getDecayedType());
@@ -2843,6 +2858,16 @@ struct ZigClangQualType ZigClangValueDecl_getType(const struct ZigClangValueDecl
     return bitcast(casted->getType());
 }
 
+struct ZigClangQualType ZigClangVectorType_getElementType(const struct ZigClangVectorType *self) {
+    auto casted = reinterpret_cast<const clang::VectorType *>(self);
+    return bitcast(casted->getElementType());
+}
+
+unsigned ZigClangVectorType_getNumElements(const struct ZigClangVectorType *self) {
+    auto casted = reinterpret_cast<const clang::VectorType *>(self);
+    return casted->getNumElements();
+}
+
 const struct ZigClangExpr *ZigClangWhileStmt_getCond(const struct ZigClangWhileStmt *self) {
     auto casted = reinterpret_cast<const clang::WhileStmt *>(self);
     return reinterpret_cast<const struct ZigClangExpr *>(casted->getCond());
@@ -2922,6 +2947,15 @@ struct ZigClangSourceLocation ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(
     return bitcast(casted->getBeginLoc());
 }
 
+unsigned ZigClangShuffleVectorExpr_getNumSubExprs(const ZigClangShuffleVectorExpr *self) {
+    auto casted = reinterpret_cast<const clang::ShuffleVectorExpr *>(self);
+    return casted->getNumSubExprs();
+}
+
+const struct ZigClangExpr *ZigClangShuffleVectorExpr_getExpr(const struct ZigClangShuffleVectorExpr *self, unsigned idx) {
+    auto casted = reinterpret_cast<const clang::ShuffleVectorExpr *>(self);
+    return reinterpret_cast<const struct ZigClangExpr *>(casted->getExpr(idx));
+}
 
 enum ZigClangUnaryExprOrTypeTrait_Kind ZigClangUnaryExprOrTypeTraitExpr_getKind(
     const struct ZigClangUnaryExprOrTypeTraitExpr *self)
src/zig_clang.h
@@ -1064,6 +1064,7 @@ ZIG_EXTERN_C bool ZigClangType_isBooleanType(const struct ZigClangType *self);
 ZIG_EXTERN_C bool ZigClangType_isVoidType(const struct ZigClangType *self);
 ZIG_EXTERN_C bool ZigClangType_isArrayType(const struct ZigClangType *self);
 ZIG_EXTERN_C bool ZigClangType_isRecordType(const struct ZigClangType *self);
+ZIG_EXTERN_C bool ZigClangType_isVectorType(const struct ZigClangType *self);
 ZIG_EXTERN_C bool ZigClangType_isIncompleteOrZeroLengthArrayType(const ZigClangQualType *self, const struct ZigClangASTContext *ctx);
 ZIG_EXTERN_C bool ZigClangType_isConstantArrayType(const ZigClangType *self);
 ZIG_EXTERN_C const char *ZigClangType_getTypeClassName(const struct ZigClangType *self);
@@ -1199,6 +1200,9 @@ ZIG_EXTERN_C const struct ZigClangExpr *ZigClangBinaryOperator_getLHS(const stru
 ZIG_EXTERN_C const struct ZigClangExpr *ZigClangBinaryOperator_getRHS(const struct ZigClangBinaryOperator *);
 ZIG_EXTERN_C struct ZigClangQualType ZigClangBinaryOperator_getType(const struct ZigClangBinaryOperator *);
 
+ZIG_EXTERN_C const struct ZigClangExpr *ZigClangConvertVectorExpr_getSrcExpr(const struct ZigClangConvertVectorExpr *);
+ZIG_EXTERN_C struct ZigClangQualType ZigClangConvertVectorExpr_getTypeSourceInfo_getType(const struct ZigClangConvertVectorExpr *);
+
 ZIG_EXTERN_C struct ZigClangQualType ZigClangDecayedType_getDecayedType(const struct ZigClangDecayedType *);
 
 ZIG_EXTERN_C const struct ZigClangCompoundStmt *ZigClangStmtExpr_getSubStmt(const struct ZigClangStmtExpr *);
@@ -1228,6 +1232,9 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangUnaryOperator_getBeginLoc(con
 
 ZIG_EXTERN_C struct ZigClangQualType ZigClangValueDecl_getType(const struct ZigClangValueDecl *);
 
+ZIG_EXTERN_C struct ZigClangQualType ZigClangVectorType_getElementType(const struct ZigClangVectorType *);
+ZIG_EXTERN_C unsigned ZigClangVectorType_getNumElements(const struct ZigClangVectorType *);
+
 ZIG_EXTERN_C const struct ZigClangExpr *ZigClangWhileStmt_getCond(const struct ZigClangWhileStmt *);
 ZIG_EXTERN_C const struct ZigClangStmt *ZigClangWhileStmt_getBody(const struct ZigClangWhileStmt *);
 
@@ -1252,6 +1259,9 @@ ZIG_EXTERN_C struct ZigClangQualType ZigClangUnaryExprOrTypeTraitExpr_getTypeOfA
 ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(const struct ZigClangUnaryExprOrTypeTraitExpr *);
 ZIG_EXTERN_C enum ZigClangUnaryExprOrTypeTrait_Kind ZigClangUnaryExprOrTypeTraitExpr_getKind(const struct ZigClangUnaryExprOrTypeTraitExpr *);
 
+ZIG_EXTERN_C unsigned ZigClangShuffleVectorExpr_getNumSubExprs(const struct ZigClangShuffleVectorExpr *);
+ZIG_EXTERN_C const struct ZigClangExpr *ZigClangShuffleVectorExpr_getExpr(const struct ZigClangShuffleVectorExpr *, unsigned);
+
 ZIG_EXTERN_C const struct ZigClangStmt *ZigClangDoStmt_getBody(const struct ZigClangDoStmt *);
 ZIG_EXTERN_C const struct ZigClangExpr *ZigClangDoStmt_getCond(const struct ZigClangDoStmt *);
 
test/run_translated_c.zig
@@ -1308,4 +1308,106 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
         \\   ufoo = (uval += 100000000);  // compile error if @truncate() not inserted
         \\}
     , "");
+
+    cases.add("basic vector expressions",
+        \\#include <stdlib.h>
+        \\#include <stdint.h>
+        \\typedef int16_t  __v8hi __attribute__((__vector_size__(16)));
+        \\int main(int argc, char**argv) {
+        \\    __v8hi uninitialized;
+        \\    __v8hi empty_init = {};
+        \\    __v8hi partial_init = {0, 1, 2, 3};
+        \\
+        \\    __v8hi a = {0, 1, 2, 3, 4, 5, 6, 7};
+        \\    __v8hi b = (__v8hi) {100, 200, 300, 400, 500, 600, 700, 800};
+        \\
+        \\    __v8hi sum = a + b;
+        \\    for (int i = 0; i < 8; i++) {
+        \\        if (sum[i] != a[i] + b[i]) abort();
+        \\    }
+        \\    return 0;
+        \\}
+    , "");
+
+    cases.add("__builtin_shufflevector",
+        \\#include <stdlib.h>
+        \\#include <stdint.h>
+        \\typedef int16_t  __v4hi __attribute__((__vector_size__(8)));
+        \\typedef int16_t  __v8hi __attribute__((__vector_size__(16)));
+        \\int main(int argc, char**argv) {
+        \\    __v8hi v8_a = {0, 1, 2, 3, 4, 5, 6, 7};
+        \\    __v8hi v8_b = {100, 200, 300, 400, 500, 600, 700, 800};
+        \\    __v8hi shuffled = __builtin_shufflevector(v8_a, v8_b, 0, 1, 2, 3, 8, 9, 10, 11);
+        \\    for (int i = 0; i < 8; i++) {
+        \\        if (i < 4) {
+        \\            if (shuffled[i] != v8_a[i]) abort();
+        \\        } else {
+        \\            if (shuffled[i] != v8_b[i - 4]) abort();
+        \\        }
+        \\    }
+        \\    shuffled = __builtin_shufflevector(
+        \\        (__v8hi) {-1, -1, -1, -1, -1, -1, -1, -1},
+        \\        (__v8hi) {42, 42, 42, 42, 42, 42, 42, 42},
+        \\        0, 1, 2, 3, 8, 9, 10, 11
+        \\    );
+        \\    for (int i = 0; i < 8; i++) {
+        \\        if (i < 4) {
+        \\            if (shuffled[i] != -1) abort();
+        \\        } else {
+        \\            if (shuffled[i] != 42) abort();
+        \\        }
+        \\    }
+        \\    __v4hi shuffled_to_fewer_elements = __builtin_shufflevector(v8_a, v8_b, 0, 1, 8, 9);
+        \\    for (int i = 0; i < 4; i++) {
+        \\        if (i < 2) {
+        \\            if (shuffled_to_fewer_elements[i] != v8_a[i]) abort();
+        \\        } else {
+        \\            if (shuffled_to_fewer_elements[i] != v8_b[i - 2]) abort();
+        \\        }
+        \\    }
+        \\    __v4hi v4_a = {0, 1, 2, 3};
+        \\    __v4hi v4_b = {100, 200, 300, 400};
+        \\    __v8hi shuffled_to_more_elements = __builtin_shufflevector(v4_a, v4_b, 0, 1, 2, 3, 4, 5, 6, 7);
+        \\    for (int i = 0; i < 4; i++) {
+        \\        if (shuffled_to_more_elements[i] != v4_a[i]) abort();
+        \\        if (shuffled_to_more_elements[i + 4] != v4_b[i]) abort();
+        \\    }
+        \\    return 0;
+        \\}
+    , "");
+
+    cases.add("__builtin_convertvector",
+        \\#include <stdlib.h>
+        \\#include <stdint.h>
+        \\typedef int16_t  __v8hi __attribute__((__vector_size__(16)));
+        \\typedef uint16_t __v8hu __attribute__((__vector_size__(16)));
+        \\int main(int argc, char**argv) {
+        \\    __v8hi signed_vector = { 1, 2, 3, 4, -1, -2, -3,-4};
+        \\    __v8hu unsigned_vector = __builtin_convertvector(signed_vector, __v8hu);
+        \\
+        \\    for (int i = 0; i < 8; i++) {
+        \\        if (unsigned_vector[i] != (uint16_t)signed_vector[i]) abort();
+        \\    }
+        \\    return 0;
+        \\}
+    , "");
+
+    cases.add("vector casting",
+        \\#include <stdlib.h>
+        \\#include <stdint.h>
+        \\typedef int8_t __v8qi __attribute__((__vector_size__(8)));
+        \\typedef uint8_t __v8qu __attribute__((__vector_size__(8)));
+        \\int main(int argc, char**argv) {
+        \\    __v8qi signed_vector = { 1, 2, 3, 4, -1, -2, -3,-4};
+        \\
+        \\    uint64_t big_int = (uint64_t) signed_vector;
+        \\    if (big_int != 0x01020304FFFEFDFCULL && big_int != 0xFCFDFEFF04030201ULL) abort();
+        \\    __v8qu unsigned_vector = (__v8qu) big_int;
+        \\    for (int i = 0; i < 8; i++) {
+        \\        if (unsigned_vector[i] != (uint8_t)signed_vector[i] && unsigned_vector[i] != (uint8_t)signed_vector[7 - i]) abort();
+        \\    }
+        \\    return 0;
+        \\}
+    , "");
+
 }