Skip to content

Commit 5937a33

Browse files
committed
translate-c: Add support for vector expressions
Includes vector types, __builtin_shufflevector, and __builtin_convertvector
1 parent 83a2665 commit 5937a33

File tree

7 files changed

+515
-8
lines changed

7 files changed

+515
-8
lines changed

lib/std/meta.zig

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,3 +1297,35 @@ pub fn globalOption(comptime name: []const u8, comptime T: type) ?T {
12971297
return null;
12981298
return @as(T, @field(root, name));
12991299
}
1300+
1301+
/// This function is for translate-c and is not intended for general use.
1302+
/// Convert from clang __builtin_shufflevector index to Zig @shuffle index
1303+
/// clang requires __builtin_shufflevector index arguments to be integer constants.
1304+
/// negative values for `this_index` indicate "don't care" so we arbitrarily choose 0
1305+
/// clang enforces that `this_index` is less than the total number of vector elements
1306+
/// See https://ziglang.org/documentation/master/#shuffle
1307+
/// See https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-shufflevector
1308+
pub fn shuffleVectorIndex(comptime this_index: c_int, comptime source_vector_len: usize) i32 {
1309+
if (this_index <= 0) return 0;
1310+
1311+
const positive_index = @intCast(usize, this_index);
1312+
if (positive_index < source_vector_len) return @intCast(i32, this_index);
1313+
const b_index = positive_index - source_vector_len;
1314+
return ~@intCast(i32, b_index);
1315+
}
1316+
1317+
test "shuffleVectorIndex" {
1318+
const vector_len: usize = 4;
1319+
1320+
testing.expect(shuffleVectorIndex(-1, vector_len) == 0);
1321+
1322+
testing.expect(shuffleVectorIndex(0, vector_len) == 0);
1323+
testing.expect(shuffleVectorIndex(1, vector_len) == 1);
1324+
testing.expect(shuffleVectorIndex(2, vector_len) == 2);
1325+
testing.expect(shuffleVectorIndex(3, vector_len) == 3);
1326+
1327+
testing.expect(shuffleVectorIndex(4, vector_len) == -1);
1328+
testing.expect(shuffleVectorIndex(5, vector_len) == -2);
1329+
testing.expect(shuffleVectorIndex(6, vector_len) == -3);
1330+
testing.expect(shuffleVectorIndex(7, vector_len) == -4);
1331+
}

src/clang.zig

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,14 @@ pub const ConstantExpr = opaque {};
300300

301301
pub const ContinueStmt = opaque {};
302302

303+
pub const ConvertVectorExpr = opaque {
304+
pub const getSrcExpr = ZigClangConvertVectorExpr_getSrcExpr;
305+
extern fn ZigClangConvertVectorExpr_getSrcExpr(*const ConvertVectorExpr) *const Expr;
306+
307+
pub const getTypeSourceInfo_getType = ZigClangConvertVectorExpr_getTypeSourceInfo_getType;
308+
extern fn ZigClangConvertVectorExpr_getTypeSourceInfo_getType(*const ConvertVectorExpr) QualType;
309+
};
310+
303311
pub const DecayedType = opaque {
304312
pub const getDecayedType = ZigClangDecayedType_getDecayedType;
305313
extern fn ZigClangDecayedType_getDecayedType(*const DecayedType) QualType;
@@ -748,6 +756,14 @@ pub const ReturnStmt = opaque {
748756
extern fn ZigClangReturnStmt_getRetValue(*const ReturnStmt) ?*const Expr;
749757
};
750758

759+
pub const ShuffleVectorExpr = opaque {
760+
pub const getNumSubExprs = ZigClangShuffleVectorExpr_getNumSubExprs;
761+
extern fn ZigClangShuffleVectorExpr_getNumSubExprs(*const ShuffleVectorExpr) c_uint;
762+
763+
pub const getExpr = ZigClangShuffleVectorExpr_getExpr;
764+
extern fn ZigClangShuffleVectorExpr_getExpr(*const ShuffleVectorExpr, c_uint) *const Expr;
765+
};
766+
751767
pub const SourceManager = opaque {
752768
pub const getSpellingLoc = ZigClangSourceManager_getSpellingLoc;
753769
extern fn ZigClangSourceManager_getSpellingLoc(*const SourceManager, Loc: SourceLocation) SourceLocation;
@@ -837,6 +853,9 @@ pub const Type = opaque {
837853
pub const isRecordType = ZigClangType_isRecordType;
838854
extern fn ZigClangType_isRecordType(*const Type) bool;
839855

856+
pub const isVectorType = ZigClangType_isVectorType;
857+
extern fn ZigClangType_isVectorType(*const Type) bool;
858+
840859
pub const isIncompleteOrZeroLengthArrayType = ZigClangType_isIncompleteOrZeroLengthArrayType;
841860
extern fn ZigClangType_isIncompleteOrZeroLengthArrayType(*const Type, *const ASTContext) bool;
842861

@@ -937,6 +956,14 @@ pub const VarDecl = opaque {
937956
extern fn ZigClangVarDecl_getTypeSourceInfo_getType(*const VarDecl) QualType;
938957
};
939958

959+
pub const VectorType = opaque {
960+
pub const getElementType = ZigClangVectorType_getElementType;
961+
extern fn ZigClangVectorType_getElementType(*const VectorType) QualType;
962+
963+
pub const getNumElements = ZigClangVectorType_getNumElements;
964+
extern fn ZigClangVectorType_getNumElements(*const VectorType) c_uint;
965+
};
966+
940967
pub const WhileStmt = opaque {
941968
pub const getCond = ZigClangWhileStmt_getCond;
942969
extern fn ZigClangWhileStmt_getCond(*const WhileStmt) *const Expr;

src/translate_c.zig

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,13 +1123,145 @@ fn transStmt(
11231123
const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt);
11241124
return transExpr(c, scope, gen_sel.getResultExpr(), result_used);
11251125
},
1126+
.ConvertVectorExprClass => {
1127+
const conv_vec = @ptrCast(*const clang.ConvertVectorExpr, stmt);
1128+
const conv_vec_node = try transConvertVectorExpr(c, scope, stmt.getBeginLoc(), conv_vec);
1129+
return maybeSuppressResult(c, scope, result_used, conv_vec_node);
1130+
},
1131+
.ShuffleVectorExprClass => {
1132+
const shuffle_vec_expr = @ptrCast(*const clang.ShuffleVectorExpr, stmt);
1133+
const shuffle_vec_node = try transShuffleVectorExpr(c, scope, shuffle_vec_expr);
1134+
return maybeSuppressResult(c, scope, result_used, shuffle_vec_node);
1135+
},
11261136
// When adding new cases here, see comment for maybeBlockify()
11271137
else => {
11281138
return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
11291139
},
11301140
}
11311141
}
11321142

1143+
/// See https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-convertvector
1144+
fn transConvertVectorExpr(
1145+
c: *Context,
1146+
scope: *Scope,
1147+
source_loc: clang.SourceLocation,
1148+
expr: *const clang.ConvertVectorExpr,
1149+
) TransError!Node {
1150+
const base_stmt = @ptrCast(*const clang.Stmt, expr);
1151+
1152+
var block_scope = try Scope.Block.init(c, scope, true);
1153+
defer block_scope.deinit();
1154+
1155+
const src_expr = expr.getSrcExpr();
1156+
const src_type = qualTypeCanon(src_expr.getType());
1157+
const src_vector_ty = @ptrCast(*const clang.VectorType, src_type);
1158+
const src_element_qt = src_vector_ty.getElementType();
1159+
const src_element_type_node = try transQualType(c, &block_scope.base, src_element_qt, base_stmt.getBeginLoc());
1160+
1161+
const src_expr_node = try transExpr(c, &block_scope.base, src_expr, .used);
1162+
1163+
const dst_qt = expr.getTypeSourceInfo_getType();
1164+
const dst_type_node = try transQualType(c, &block_scope.base, dst_qt, base_stmt.getBeginLoc());
1165+
const dst_vector_ty = @ptrCast(*const clang.VectorType, qualTypeCanon(dst_qt));
1166+
const num_elements = dst_vector_ty.getNumElements();
1167+
const dst_element_qt = dst_vector_ty.getElementType();
1168+
1169+
// workaround for https://github.com/ziglang/zig/issues/8322
1170+
// we store the casted results into temp variables and use those
1171+
// to initialize the vector. Eventually we can just directly
1172+
// construct the init_list from casted source members
1173+
var i: usize = 0;
1174+
while (i < num_elements) : (i += 1) {
1175+
const mangled_name = try block_scope.makeMangledName(c, "tmp");
1176+
const value = try Tag.array_access.create(c.arena, .{
1177+
.lhs = src_expr_node,
1178+
.rhs = try transCreateNodeNumber(c, i, .int),
1179+
});
1180+
const tmp_decl_node = try Tag.var_simple.create(c.arena, .{
1181+
.name = mangled_name,
1182+
.init = try transCCast(c, &block_scope.base, base_stmt.getBeginLoc(), dst_element_qt, src_element_qt, value),
1183+
});
1184+
try block_scope.statements.append(tmp_decl_node);
1185+
}
1186+
1187+
const init_list = try c.arena.alloc(Node, num_elements);
1188+
for (init_list) |*init, init_index| {
1189+
const tmp_decl = block_scope.statements.items[init_index];
1190+
const name = tmp_decl.castTag(.var_simple).?.data.name;
1191+
init.* = try Tag.identifier.create(c.arena, name);
1192+
}
1193+
1194+
const vec_init = try Tag.array_init.create(c.arena, .{
1195+
.cond = dst_type_node,
1196+
.cases = init_list,
1197+
});
1198+
1199+
const break_node = try Tag.break_val.create(c.arena, .{
1200+
.label = block_scope.label,
1201+
.val = vec_init,
1202+
});
1203+
try block_scope.statements.append(break_node);
1204+
return block_scope.complete(c);
1205+
}
1206+
1207+
fn makeShuffleMask(c: *Context, scope: *Scope, expr: *const clang.ShuffleVectorExpr, vector_len: Node) TransError!Node {
1208+
const num_subexprs = expr.getNumSubExprs();
1209+
assert(num_subexprs >= 3); // two source vectors + at least 1 index expression
1210+
const mask_len = num_subexprs - 2;
1211+
1212+
const mask_type = try Tag.std_meta_vector.create(c.arena, .{
1213+
.lhs = try transCreateNodeNumber(c, mask_len, .int),
1214+
.rhs = try Tag.type.create(c.arena, "i32"),
1215+
});
1216+
1217+
const init_list = try c.arena.alloc(Node, mask_len);
1218+
1219+
for (init_list) |*init, i| {
1220+
const index_expr = try transExprCoercing(c, scope, expr.getExpr(@intCast(c_uint, i + 2)), .used);
1221+
const converted_index = try Tag.std_meta_shuffle_vector_index.create(c.arena, .{ .lhs = index_expr, .rhs = vector_len });
1222+
init.* = converted_index;
1223+
}
1224+
1225+
const mask_init = try Tag.array_init.create(c.arena, .{
1226+
.cond = mask_type,
1227+
.cases = init_list,
1228+
});
1229+
return Tag.@"comptime".create(c.arena, mask_init);
1230+
}
1231+
1232+
/// @typeInfo(@TypeOf(vec_node)).Vector.<field>
1233+
fn vectorTypeInfo(arena: *mem.Allocator, vec_node: Node, field: []const u8) TransError!Node {
1234+
const typeof_call = try Tag.typeof.create(arena, vec_node);
1235+
const typeinfo_call = try Tag.typeinfo.create(arena, typeof_call);
1236+
const vector_type_info = try Tag.field_access.create(arena, .{ .lhs = typeinfo_call, .field_name = "Vector" });
1237+
return Tag.field_access.create(arena, .{ .lhs = vector_type_info, .field_name = field });
1238+
}
1239+
1240+
fn transShuffleVectorExpr(
1241+
c: *Context,
1242+
scope: *Scope,
1243+
expr: *const clang.ShuffleVectorExpr,
1244+
) TransError!Node {
1245+
const base_expr = @ptrCast(*const clang.Expr, expr);
1246+
const num_subexprs = expr.getNumSubExprs();
1247+
if (num_subexprs < 3) return fail(c, error.UnsupportedTranslation, base_expr.getBeginLoc(), "ShuffleVector needs at least 1 index", .{});
1248+
1249+
const a = try transExpr(c, scope, expr.getExpr(0), .used);
1250+
const b = try transExpr(c, scope, expr.getExpr(1), .used);
1251+
1252+
// clang requires first two arguments to __builtin_shufflevector to be same type
1253+
const vector_child_type = try vectorTypeInfo(c.arena, a, "child");
1254+
const vector_len = try vectorTypeInfo(c.arena, a, "len");
1255+
const shuffle_mask = try makeShuffleMask(c, scope, expr, vector_len);
1256+
1257+
return Tag.shuffle.create(c.arena, .{
1258+
.element_type = vector_child_type,
1259+
.a = a,
1260+
.b = b,
1261+
.mask_vector = shuffle_mask,
1262+
});
1263+
}
1264+
11331265
/// Translate a "simple" offsetof expression containing exactly one component,
11341266
/// when that component is of kind .Field - e.g. offsetof(mytype, myfield)
11351267
fn transSimpleOffsetOfExpr(
@@ -1935,6 +2067,10 @@ fn cIsEnum(qt: clang.QualType) bool {
19352067
return qt.getCanonicalType().getTypeClass() == .Enum;
19362068
}
19372069

2070+
fn cIsVector(qt: clang.QualType) bool {
2071+
return qt.getCanonicalType().getTypeClass() == .Vector;
2072+
}
2073+
19382074
/// Get the underlying int type of an enum. The C compiler chooses a signed int
19392075
/// type that is large enough to hold all of the enum's values. It is not required
19402076
/// to be the smallest possible type that can hold all the values.
@@ -1991,6 +2127,11 @@ fn transCCast(
19912127
// @bitCast(dest_type, intermediate_value)
19922128
return Tag.bit_cast.create(c.arena, .{ .lhs = dst_node, .rhs = src_int_expr });
19932129
}
2130+
if (cIsVector(src_type) or cIsVector(dst_type)) {
2131+
// C cast where at least 1 operand is a vector requires them to be same size
2132+
// @bitCast(dest_type, val)
2133+
return Tag.bit_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
2134+
}
19942135
if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) {
19952136
// @intCast(dest_type, @ptrToInt(val))
19962137
const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr);
@@ -2209,6 +2350,63 @@ fn transInitListExprArray(
22092350
}
22102351
}
22112352

2353+
fn transInitListExprVector(
2354+
c: *Context,
2355+
scope: *Scope,
2356+
loc: clang.SourceLocation,
2357+
expr: *const clang.InitListExpr,
2358+
ty: *const clang.Type,
2359+
) TransError!Node {
2360+
2361+
const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr));
2362+
const vector_type = try transQualType(c, scope, qt, loc);
2363+
const init_count = expr.getNumInits();
2364+
2365+
if (init_count == 0) {
2366+
return Tag.container_init.create(c.arena, .{
2367+
.lhs = vector_type,
2368+
.inits = try c.arena.alloc(ast.Payload.ContainerInit.Initializer, 0),
2369+
});
2370+
}
2371+
2372+
var block_scope = try Scope.Block.init(c, scope, true);
2373+
defer block_scope.deinit();
2374+
2375+
// workaround for https://github.com/ziglang/zig/issues/8322
2376+
// we store the initializers in temp variables and use those
2377+
// to initialize the vector. Eventually we can just directly
2378+
// construct the init_list from casted source members
2379+
var i: usize = 0;
2380+
while (i < init_count) : (i += 1) {
2381+
const mangled_name = try block_scope.makeMangledName(c, "tmp");
2382+
const init_expr = expr.getInit(@intCast(c_uint, i));
2383+
const tmp_decl_node = try Tag.var_simple.create(c.arena, .{
2384+
.name = mangled_name,
2385+
.init = try transExpr(c, &block_scope.base, init_expr, .used),
2386+
});
2387+
try block_scope.statements.append(tmp_decl_node);
2388+
}
2389+
2390+
const init_list = try c.arena.alloc(Node, init_count);
2391+
for (init_list) |*init, init_index| {
2392+
const tmp_decl = block_scope.statements.items[init_index];
2393+
const name = tmp_decl.castTag(.var_simple).?.data.name;
2394+
init.* = try Tag.identifier.create(c.arena, name);
2395+
}
2396+
2397+
const array_init = try Tag.array_init.create(c.arena, .{
2398+
.cond = vector_type,
2399+
.cases = init_list,
2400+
});
2401+
const break_node = try Tag.break_val.create(c.arena, .{
2402+
.label = block_scope.label,
2403+
.val = array_init,
2404+
});
2405+
try block_scope.statements.append(break_node);
2406+
2407+
return block_scope.complete(c);
2408+
}
2409+
22122410
fn transInitListExpr(
22132411
c: *Context,
22142412
scope: *Scope,
@@ -2235,6 +2433,14 @@ fn transInitListExpr(
22352433
expr,
22362434
qual_type,
22372435
));
2436+
} else if (qual_type.isVectorType()) {
2437+
return maybeSuppressResult(c, scope, used, try transInitListExprVector(
2438+
c,
2439+
scope,
2440+
source_loc,
2441+
expr,
2442+
qual_type,
2443+
));
22382444
} else {
22392445
const type_name = c.str(qual_type.getTypeClassName());
22402446
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
40854291
};
40864292
return Tag.typeof.create(c.arena, underlying_expr);
40874293
},
4294+
.Vector => {
4295+
const vector_ty = @ptrCast(*const clang.VectorType, ty);
4296+
const num_elements = vector_ty.getNumElements();
4297+
const element_qt = vector_ty.getElementType();
4298+
return Tag.std_meta_vector.create(c.arena, .{
4299+
.lhs = try transCreateNodeNumber(c, num_elements, .int),
4300+
.rhs = try transQualType(c, scope, element_qt, source_loc),
4301+
});
4302+
},
40884303
else => {
40894304
const type_name = c.str(ty.getTypeClassName());
40904305
return fail(c, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name});

0 commit comments

Comments
 (0)