spirv-reader: fix signedness for shifts

Fixed: tint:675
Change-Id: Ib754191284a62b9f4be56dc8d38e5319345ab9bf
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49824
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
Reviewed-by: Alan Baker <alanbaker@google.com>
This commit is contained in:
David Neto 2021-05-06 16:21:53 +00:00 committed by Commit Bot service account
parent b82acac68e
commit e21ad1438e
4 changed files with 576 additions and 99 deletions

View File

@ -210,11 +210,6 @@ ast::BinaryOp ConvertBinaryOp(SpvOp opcode) {
case SpvOpSMod:
case SpvOpFMod:
return ast::BinaryOp::kModulo;
case SpvOpShiftLeftLogical:
return ast::BinaryOp::kShiftLeft;
case SpvOpShiftRightLogical:
case SpvOpShiftRightArithmetic:
return ast::BinaryOp::kShiftRight;
case SpvOpLogicalEqual:
case SpvOpIEqual:
case SpvOpFOrdEqual:
@ -3345,6 +3340,34 @@ TypedExpression FunctionEmitter::MaybeEmitCombinatorialValue(
Source{}, ast_type, MakeOperand(inst, 0).expr)};
}
if (opcode == SpvOpShiftLeftLogical || opcode == SpvOpShiftRightLogical ||
opcode == SpvOpShiftRightArithmetic) {
auto arg0 = MakeOperand(inst, 0);
// The second operand must be unsigned. It's ok to wrap the shift amount
// since the shift is modulo the bit width of the first operand.
auto arg1 = parser_impl_.AsUnsigned(MakeOperand(inst, 1));
switch (opcode) {
case SpvOpShiftLeftLogical:
binary_op = ast::BinaryOp::kShiftLeft;
break;
case SpvOpShiftRightLogical:
arg0 = parser_impl_.AsUnsigned(arg0);
binary_op = ast::BinaryOp::kShiftRight;
break;
case SpvOpShiftRightArithmetic:
arg0 = parser_impl_.AsSigned(arg0);
binary_op = ast::BinaryOp::kShiftRight;
break;
default:
break;
}
TypedExpression result{
ast_type, create<ast::BinaryExpression>(Source{}, binary_op, arg0.expr,
arg1.expr)};
return parser_impl_.RectifyForcedResultType(result, inst, arg0.type);
}
auto negated_op = NegatedFloatCompare(opcode);
if (negated_op != ast::BinaryOp::kNone) {
auto arg0 = MakeOperand(inst, 0);

View File

@ -203,7 +203,7 @@ TEST_P(SpvBinaryBitGeneralTest, EmitExpression) {
<< p->error() << "\n"
<< assembly;
auto fe = p->function_emitter(100);
EXPECT_TRUE(fe.EmitBody()) << p->error();
EXPECT_TRUE(fe.EmitBody()) << p->error() << assembly;
std::ostringstream ss;
ss << R"(VariableConst{
x_1
@ -215,115 +215,536 @@ TEST_P(SpvBinaryBitGeneralTest, EmitExpression) {
}
INSTANTIATE_TEST_SUITE_P(
SpvParserTest_ShiftLeftLogical,
SpvParserTest_ShiftLeftLogical_Arg2Unsigned,
SpvBinaryBitTest,
::testing::Values(
// Both uint
// uint uint -> uint
BinaryData{"uint", "uint_10", "OpShiftLeftLogical", "uint_20", "__u32",
"ScalarConstructor[not set]{10u}", "shift_left",
"ScalarConstructor[not set]{20u}"},
// Both int
BinaryData{"int", "int_30", "OpShiftLeftLogical", "int_40", "__i32",
// int, uint -> int
BinaryData{"int", "int_30", "OpShiftLeftLogical", "uint_20", "__i32",
"ScalarConstructor[not set]{30}", "shift_left",
"ScalarConstructor[not set]{40}"},
// Mixed, returning uint
BinaryData{"uint", "int_30", "OpShiftLeftLogical", "uint_10", "__u32",
"ScalarConstructor[not set]{30}", "shift_left",
"ScalarConstructor[not set]{10u}"},
// Mixed, returning int
BinaryData{"int", "int_30", "OpShiftLeftLogical", "uint_10", "__i32",
"ScalarConstructor[not set]{30}", "shift_left",
"ScalarConstructor[not set]{10u}"},
// Both v2uint
"ScalarConstructor[not set]{20u}"},
// v2uint v2uint -> v2uint
BinaryData{"v2uint", "v2uint_10_20", "OpShiftLeftLogical",
"v2uint_20_10", "__vec_2__u32", AstFor("v2uint_10_20"),
"shift_left", AstFor("v2uint_20_10")},
// Both v2int
BinaryData{"v2int", "v2int_30_40", "OpShiftLeftLogical", "v2int_40_30",
// v2int, v2uint -> v2int
BinaryData{"v2int", "v2int_30_40", "OpShiftLeftLogical", "v2uint_20_10",
"__vec_2__i32", AstFor("v2int_30_40"), "shift_left",
AstFor("v2int_40_30")},
// Mixed, returning v2uint
BinaryData{"v2uint", "v2int_30_40", "OpShiftLeftLogical",
"v2uint_10_20", "__vec_2__u32", AstFor("v2int_30_40"),
"shift_left", AstFor("v2uint_10_20")},
// Mixed, returning v2int
BinaryData{"v2int", "v2int_40_30", "OpShiftLeftLogical", "v2uint_20_10",
"__vec_2__i32", AstFor("v2int_40_30"), "shift_left",
AstFor("v2uint_20_10")}));
INSTANTIATE_TEST_SUITE_P(
SpvParserTest_ShiftRightLogical,
SpvBinaryBitTest,
// WGSL requires second operand to be unsigned, so insert bitcasts
SpvParserTest_ShiftLeftLogical_Arg2Signed,
SpvBinaryBitGeneralTest,
::testing::Values(
// Both uint
BinaryData{"uint", "uint_10", "OpShiftRightLogical", "uint_20", "__u32",
"ScalarConstructor[not set]{10u}", "shift_right",
"ScalarConstructor[not set]{20u}"},
// Both int
BinaryData{"int", "int_30", "OpShiftRightLogical", "int_40", "__i32",
"ScalarConstructor[not set]{30}", "shift_right",
"ScalarConstructor[not set]{40}"},
// Mixed, returning uint
BinaryData{"uint", "int_30", "OpShiftRightLogical", "uint_10", "__u32",
"ScalarConstructor[not set]{30}", "shift_right",
"ScalarConstructor[not set]{10u}"},
// Mixed, returning int
BinaryData{"int", "int_30", "OpShiftRightLogical", "uint_10", "__i32",
"ScalarConstructor[not set]{30}", "shift_right",
"ScalarConstructor[not set]{10u}"},
// Both v2uint
BinaryData{"v2uint", "v2uint_10_20", "OpShiftRightLogical",
"v2uint_20_10", "__vec_2__u32", AstFor("v2uint_10_20"),
"shift_right", AstFor("v2uint_20_10")},
// Both v2int
BinaryData{"v2int", "v2int_30_40", "OpShiftRightLogical", "v2int_40_30",
"__vec_2__i32", AstFor("v2int_30_40"), "shift_right",
AstFor("v2int_40_30")},
// Mixed, returning v2uint
BinaryData{"v2uint", "v2int_30_40", "OpShiftRightLogical",
"v2uint_10_20", "__vec_2__u32", AstFor("v2int_30_40"),
"shift_right", AstFor("v2uint_10_20")},
// Mixed, returning v2int
BinaryData{"v2int", "v2int_40_30", "OpShiftRightLogical",
"v2uint_20_10", "__vec_2__i32", AstFor("v2int_40_30"),
"shift_right", AstFor("v2uint_20_10")}));
// int, int -> int
BinaryDataGeneral{"int", "int_30", "OpShiftLeftLogical", "int_40",
R"(__i32
{
Binary[not set]{
ScalarConstructor[not set]{30}
shift_left
Bitcast[not set]<__u32>{
ScalarConstructor[not set]{40}
}
}
}
)"},
// uint, int -> uint
BinaryDataGeneral{"uint", "uint_10", "OpShiftLeftLogical", "int_40",
R"(__u32
{
Binary[not set]{
ScalarConstructor[not set]{10u}
shift_left
Bitcast[not set]<__u32>{
ScalarConstructor[not set]{40}
}
}
}
)"},
// v2uint, v2int -> v2uint
BinaryDataGeneral{"v2uint", "v2uint_10_20", "OpShiftLeftLogical",
"v2uint_20_10",
R"(__vec_2__u32
{
Binary[not set]{
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{10u}
ScalarConstructor[not set]{20u}
}
shift_left
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{20u}
ScalarConstructor[not set]{10u}
}
}
}
)"},
// v2int, v2int -> v2int
BinaryDataGeneral{"v2int", "v2int_30_40", "OpShiftLeftLogical",
"v2int_40_30",
R"(__vec_2__i32
{
Binary[not set]{
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{30}
ScalarConstructor[not set]{40}
}
shift_left
Bitcast[not set]<__vec_2__u32>{
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{40}
ScalarConstructor[not set]{30}
}
}
}
}
)"}));
INSTANTIATE_TEST_SUITE_P(
SpvParserTest_ShiftRightArithmetic,
SpvBinaryBitTest,
SpvParserTest_ShiftLeftLogical_BitcastResult,
SpvBinaryBitGeneralTest,
::testing::Values(
// Both uint
BinaryData{"uint", "uint_10", "OpShiftRightArithmetic", "uint_20",
"__u32", "ScalarConstructor[not set]{10u}", "shift_right",
"ScalarConstructor[not set]{20u}"},
// Both int
BinaryData{"int", "int_30", "OpShiftRightArithmetic", "int_40", "__i32",
"ScalarConstructor[not set]{30}", "shift_right",
"ScalarConstructor[not set]{40}"},
// Mixed, returning uint
BinaryData{"uint", "int_30", "OpShiftRightArithmetic", "uint_10",
"__u32", "ScalarConstructor[not set]{30}", "shift_right",
"ScalarConstructor[not set]{10u}"},
// Mixed, returning int
BinaryData{"int", "int_30", "OpShiftRightArithmetic", "uint_10",
"__i32", "ScalarConstructor[not set]{30}", "shift_right",
"ScalarConstructor[not set]{10u}"},
// Both v2uint
BinaryData{"v2uint", "v2uint_10_20", "OpShiftRightArithmetic",
"v2uint_20_10", "__vec_2__u32", AstFor("v2uint_10_20"),
"shift_right", AstFor("v2uint_20_10")},
// Both v2int
BinaryData{"v2int", "v2int_30_40", "OpShiftRightArithmetic",
"v2int_40_30", "__vec_2__i32", AstFor("v2int_30_40"),
"shift_right", AstFor("v2int_40_30")},
// Mixed, returning v2uint
BinaryData{"v2uint", "v2int_30_40", "OpShiftRightArithmetic",
"v2uint_10_20", "__vec_2__u32", AstFor("v2int_30_40"),
"shift_right", AstFor("v2uint_10_20")},
// Mixed, returning v2int
BinaryData{"v2int", "v2int_40_30", "OpShiftRightArithmetic",
"v2uint_20_10", "__vec_2__i32", AstFor("v2int_40_30"),
"shift_right", AstFor("v2uint_20_10")}));
// int, int -> uint
BinaryDataGeneral{"uint", "int_30", "OpShiftLeftLogical", "uint_10",
R"(__u32
{
Bitcast[not set]<__u32>{
Binary[not set]{
ScalarConstructor[not set]{30}
shift_left
ScalarConstructor[not set]{10u}
}
}
}
)"},
// v2uint, v2int -> v2uint
BinaryDataGeneral{"v2uint", "v2int_30_40", "OpShiftLeftLogical",
"v2uint_20_10",
R"(__vec_2__u32
{
Bitcast[not set]<__vec_2__u32>{
Binary[not set]{
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{30}
ScalarConstructor[not set]{40}
}
shift_left
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{20u}
ScalarConstructor[not set]{10u}
}
}
}
}
)"}));
INSTANTIATE_TEST_SUITE_P(
SpvParserTest_ShiftRightLogical_Arg2Unsigned,
SpvBinaryBitGeneralTest,
::testing::Values(
// uint, uint -> uint
BinaryDataGeneral{"uint", "uint_10", "OpShiftRightLogical", "uint_20",
R"(__u32
{
Binary[not set]{
ScalarConstructor[not set]{10u}
shift_right
ScalarConstructor[not set]{20u}
}
}
)"},
// int, uint -> int
BinaryDataGeneral{"int", "int_30", "OpShiftRightLogical", "uint_20",
R"(__i32
{
Bitcast[not set]<__i32>{
Binary[not set]{
Bitcast[not set]<__u32>{
ScalarConstructor[not set]{30}
}
shift_right
ScalarConstructor[not set]{20u}
}
}
}
)"},
// v2uint, v2uint -> v2uint
BinaryDataGeneral{"v2uint", "v2uint_10_20", "OpShiftRightLogical",
"v2uint_20_10",
R"(__vec_2__u32
{
Binary[not set]{
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{10u}
ScalarConstructor[not set]{20u}
}
shift_right
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{20u}
ScalarConstructor[not set]{10u}
}
}
}
)"},
// v2int, v2uint -> v2int
BinaryDataGeneral{"v2int", "v2int_30_40", "OpShiftRightLogical",
"v2uint_10_20",
R"(__vec_2__i32
{
Bitcast[not set]<__vec_2__i32>{
Binary[not set]{
Bitcast[not set]<__vec_2__u32>{
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{30}
ScalarConstructor[not set]{40}
}
}
shift_right
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{10u}
ScalarConstructor[not set]{20u}
}
}
}
}
)"}));
INSTANTIATE_TEST_SUITE_P(
SpvParserTest_ShiftRightLogical_Arg2Signed,
SpvBinaryBitGeneralTest,
::testing::Values(
// uint, int -> uint
BinaryDataGeneral{"uint", "uint_10", "OpShiftRightLogical", "int_30",
R"(__u32
{
Binary[not set]{
ScalarConstructor[not set]{10u}
shift_right
Bitcast[not set]<__u32>{
ScalarConstructor[not set]{30}
}
}
}
)"},
// int, int -> int
BinaryDataGeneral{"int", "int_30", "OpShiftRightLogical", "int_40",
R"(__i32
{
Bitcast[not set]<__i32>{
Binary[not set]{
Bitcast[not set]<__u32>{
ScalarConstructor[not set]{30}
}
shift_right
Bitcast[not set]<__u32>{
ScalarConstructor[not set]{40}
}
}
}
}
)"},
// v2uint, v2int -> v2uint
BinaryDataGeneral{"v2uint", "v2uint_10_20", "OpShiftRightLogical",
"v2int_30_40",
R"(__vec_2__u32
{
Binary[not set]{
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{10u}
ScalarConstructor[not set]{20u}
}
shift_right
Bitcast[not set]<__vec_2__u32>{
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{30}
ScalarConstructor[not set]{40}
}
}
}
}
)"},
// v2int, v2int -> v2int
BinaryDataGeneral{"v2int", "v2int_40_30", "OpShiftRightLogical",
"v2int_30_40",
R"(__vec_2__i32
{
Bitcast[not set]<__vec_2__i32>{
Binary[not set]{
Bitcast[not set]<__vec_2__u32>{
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{40}
ScalarConstructor[not set]{30}
}
}
shift_right
Bitcast[not set]<__vec_2__u32>{
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{30}
ScalarConstructor[not set]{40}
}
}
}
}
}
)"}));
INSTANTIATE_TEST_SUITE_P(
SpvParserTest_ShiftRightLogical_BitcastResult,
SpvBinaryBitGeneralTest,
::testing::Values(
// uint, uint -> int
BinaryDataGeneral{"int", "uint_20", "OpShiftRightLogical", "uint_10",
R"(__i32
{
Bitcast[not set]<__i32>{
Binary[not set]{
ScalarConstructor[not set]{20u}
shift_right
ScalarConstructor[not set]{10u}
}
}
}
)"},
// v2uint, v2uint -> v2int
BinaryDataGeneral{"v2int", "v2uint_10_20", "OpShiftRightLogical",
"v2uint_20_10",
R"(__vec_2__i32
{
Bitcast[not set]<__vec_2__i32>{
Binary[not set]{
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{10u}
ScalarConstructor[not set]{20u}
}
shift_right
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{20u}
ScalarConstructor[not set]{10u}
}
}
}
}
)"}));
INSTANTIATE_TEST_SUITE_P(
SpvParserTest_ShiftRightArithmetic_Arg2Unsigned,
SpvBinaryBitGeneralTest,
::testing::Values(
// uint, uint -> uint
BinaryDataGeneral{"uint", "uint_10", "OpShiftRightArithmetic",
"uint_20",
R"(__u32
{
Bitcast[not set]<__u32>{
Binary[not set]{
Bitcast[not set]<__i32>{
ScalarConstructor[not set]{10u}
}
shift_right
ScalarConstructor[not set]{20u}
}
}
}
)"},
// int, uint -> int
BinaryDataGeneral{"int", "int_30", "OpShiftRightArithmetic", "uint_10",
R"(__i32
{
Binary[not set]{
ScalarConstructor[not set]{30}
shift_right
ScalarConstructor[not set]{10u}
}
}
)"},
// v2uint, v2uint -> v2uint
BinaryDataGeneral{"v2uint", "v2uint_10_20", "OpShiftRightArithmetic",
"v2uint_20_10",
R"(__vec_2__u32
{
Bitcast[not set]<__vec_2__u32>{
Binary[not set]{
Bitcast[not set]<__vec_2__i32>{
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{10u}
ScalarConstructor[not set]{20u}
}
}
shift_right
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{20u}
ScalarConstructor[not set]{10u}
}
}
}
}
)"},
// v2int, v2uint -> v2int
BinaryDataGeneral{"v2int", "v2int_40_30", "OpShiftRightArithmetic",
"v2uint_20_10",
R"(__vec_2__i32
{
Binary[not set]{
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{40}
ScalarConstructor[not set]{30}
}
shift_right
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{20u}
ScalarConstructor[not set]{10u}
}
}
}
)"}));
INSTANTIATE_TEST_SUITE_P(
SpvParserTest_ShiftRightArithmetic_Arg2Signed,
SpvBinaryBitGeneralTest,
::testing::Values(
// uint, int -> uint
BinaryDataGeneral{"uint", "uint_10", "OpShiftRightArithmetic", "int_30",
R"(__u32
{
Bitcast[not set]<__u32>{
Binary[not set]{
Bitcast[not set]<__i32>{
ScalarConstructor[not set]{10u}
}
shift_right
Bitcast[not set]<__u32>{
ScalarConstructor[not set]{30}
}
}
}
}
)"},
// int, int -> int
BinaryDataGeneral{"int", "int_30", "OpShiftRightArithmetic", "int_40",
R"(__i32
{
Binary[not set]{
ScalarConstructor[not set]{30}
shift_right
Bitcast[not set]<__u32>{
ScalarConstructor[not set]{40}
}
}
}
)"},
// v2uint, v2int -> v2uint
BinaryDataGeneral{"v2uint", "v2uint_10_20", "OpShiftRightArithmetic",
"v2int_30_40",
R"(__vec_2__u32
{
Bitcast[not set]<__vec_2__u32>{
Binary[not set]{
Bitcast[not set]<__vec_2__i32>{
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{10u}
ScalarConstructor[not set]{20u}
}
}
shift_right
Bitcast[not set]<__vec_2__u32>{
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{30}
ScalarConstructor[not set]{40}
}
}
}
}
}
)"},
// v2int, v2int -> v2int
BinaryDataGeneral{"v2int", "v2int_40_30", "OpShiftRightArithmetic",
"v2int_30_40",
R"(__vec_2__i32
{
Binary[not set]{
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{40}
ScalarConstructor[not set]{30}
}
shift_right
Bitcast[not set]<__vec_2__u32>{
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{30}
ScalarConstructor[not set]{40}
}
}
}
}
)"}));
INSTANTIATE_TEST_SUITE_P(
SpvParserTest_ShiftRightArithmetic_BitcastResult,
SpvBinaryBitGeneralTest,
::testing::Values(
// int, uint -> uint
BinaryDataGeneral{"uint", "int_30", "OpShiftRightArithmetic", "uint_10",
R"(__u32
{
Bitcast[not set]<__u32>{
Binary[not set]{
ScalarConstructor[not set]{30}
shift_right
ScalarConstructor[not set]{10u}
}
}
}
)"},
// v2int, v2uint -> v2uint
BinaryDataGeneral{"v2uint", "v2int_30_40", "OpShiftRightArithmetic",
"v2uint_20_10",
R"(__vec_2__u32
{
Bitcast[not set]<__vec_2__u32>{
Binary[not set]{
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{30}
ScalarConstructor[not set]{40}
}
shift_right
TypeConstructor[not set]{
__vec_2__u32
ScalarConstructor[not set]{20u}
ScalarConstructor[not set]{10u}
}
}
}
}
)"}));
INSTANTIATE_TEST_SUITE_P(
SpvParserTest_BitwiseAnd,

View File

@ -199,6 +199,9 @@ bool AssumesResultSignednessMatchesFirstOperand(SpvOp opcode) {
case SpvOpBitwiseAnd:
case SpvOpBitwiseOr:
case SpvOpBitwiseXor:
case SpvOpShiftLeftLogical:
case SpvOpShiftRightLogical:
case SpvOpShiftRightArithmetic:
return true;
default:
break;
@ -1771,6 +1774,24 @@ TypedExpression ParserImpl::RectifyForcedResultType(
create<ast::BitcastExpression>(Source{}, expr.type, expr.expr)};
}
TypedExpression ParserImpl::AsUnsigned(TypedExpression expr) {
if (expr.type && expr.type->is_signed_scalar_or_vector()) {
auto new_type = GetUnsignedIntMatchingShape(expr.type);
return {new_type,
create<ast::BitcastExpression>(Source{}, new_type, expr.expr)};
}
return expr;
}
TypedExpression ParserImpl::AsSigned(TypedExpression expr) {
if (expr.type && expr.type->is_unsigned_scalar_or_vector()) {
auto new_type = GetSignedIntMatchingShape(expr.type);
return {new_type,
create<ast::BitcastExpression>(Source{}, new_type, expr.expr)};
}
return expr;
}
bool ParserImpl::EmitFunctions() {
if (!success_) {
return false;

View File

@ -354,7 +354,7 @@ class ParserImpl : Reader {
const spvtools::opt::Instruction& inst,
TypedExpression&& expr);
/// Conversts a second operand to the signedness of the first operand
/// Converts a second operand to the signedness of the first operand
/// of a binary operator, if the WGSL operator requires they be the same.
/// Returns the converted expression, or the original expression if the
/// conversion is not needed.
@ -407,6 +407,18 @@ class ParserImpl : Reader {
const spvtools::opt::Instruction& inst,
typ::Type first_operand_type);
/// Returns the given expression, but ensuring it's an unsigned type of the
/// same shape as the operand. Wraps the expresison with a bitcast if needed.
/// Assumes the given expresion is a integer scalar or vector.
/// @param expr an integer scalar or integer vector expression.
TypedExpression AsUnsigned(TypedExpression expr);
/// Returns the given expression, but ensuring it's a signed type of the
/// same shape as the operand. Wraps the expresison with a bitcast if needed.
/// Assumes the given expresion is a integer scalar or vector.
/// @param expr an integer scalar or integer vector expression.
TypedExpression AsSigned(TypedExpression expr);
/// Bookkeeping used for tracking the "position" builtin variable.
struct BuiltInPositionInfo {
/// The ID for the gl_PerVertex struct containing the Position builtin.