Skip to content

translate-c: Add support for vector expressions #8389

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions lib/std/meta.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
27 changes: 27 additions & 0 deletions src/clang.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
215 changes: 215 additions & 0 deletions src/translate_c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1123,13 +1123,145 @@ 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)});
},
}
}

/// 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(
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand All @@ -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});
Expand Down Expand Up @@ -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});
Expand Down
Loading