Skip to content

Commit de4557f

Browse files
committed
C front-end: __builtin_shuffle, __builtin_shufflevector
These are polymorphic built-ins supported by GCC and Clang, respectively. Introduce a new shuffle_vector expression to handle these, with type checking addressing the differences between the GCC/Clang APIs.
1 parent be4e768 commit de4557f

11 files changed

+367
-3
lines changed

regression/cbmc/gcc_vector3/main.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#include <assert.h>
2+
3+
#ifdef __GNUC__
4+
typedef int v4si __attribute__((vector_size(16)));
5+
6+
typedef union {
7+
v4si v;
8+
int members[4];
9+
} vector_u;
10+
#endif
11+
12+
int main()
13+
{
14+
#if defined(__GNUC__) && !defined(__clang__)
15+
// https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html
16+
v4si a = {1, 2, 3, 4};
17+
v4si b = {5, 6, 7, 8};
18+
v4si mask1 = {0, 1, 5, 3};
19+
v4si mask2 = {0, 4, 2, 5};
20+
21+
vector_u res;
22+
23+
res.v = __builtin_shuffle(a, mask1);
24+
assert(res.members[0] == 1);
25+
assert(res.members[1] == 2);
26+
assert(res.members[2] == 2);
27+
assert(res.members[3] == 4);
28+
29+
res.v = __builtin_shuffle(a, b, mask2);
30+
assert(res.members[0] == 1);
31+
assert(res.members[1] == 5);
32+
assert(res.members[2] == 3);
33+
assert(res.members[3] == 6);
34+
#elif defined(__clang__)
35+
v4si a = {1, 2, 3, 4};
36+
v4si b = {5, 6, 7, 8};
37+
38+
vector_u res;
39+
40+
res.v = __builtin_shufflevector(a, a, 0, 1, -1, 3);
41+
assert(res.members[0] == 1);
42+
assert(res.members[1] == 2);
43+
// res.members[2] is "don't care"
44+
assert(res.members[3] == 4);
45+
46+
res.v = __builtin_shufflevector(a, b, 0, 4, 2, 5);
47+
assert(res.members[0] == 1);
48+
assert(res.members[1] == 5);
49+
assert(res.members[2] == 3);
50+
assert(res.members[3] == 6);
51+
#endif
52+
}

regression/cbmc/gcc_vector3/test.desc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE broken-smt-backend
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

scripts/expected_doxygen_warnings.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
warning: Include graph for 'goto_instrument_parse_options.cpp' not generated, too many nodes (97), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
2222
warning: Included by graph for 'goto_functions.h' not generated, too many nodes (66), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
2323
warning: Included by graph for 'goto_model.h' not generated, too many nodes (111), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
24-
warning: Included by graph for 'arith_tools.h' not generated, too many nodes (182), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
24+
warning: Included by graph for 'arith_tools.h' not generated, too many nodes (183), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
2525
warning: Included by graph for 'c_types.h' not generated, too many nodes (142), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
2626
warning: Included by graph for 'config.h' not generated, too many nodes (88), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
2727
warning: Included by graph for 'exception_utils.h' not generated, too many nodes (61), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.

src/ansi-c/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ SRC = anonymous_member.cpp \
1111
ansi_c_typecheck.cpp \
1212
ansi_c_y.tab.cpp \
1313
builtin_factory.cpp \
14+
c_expr.cpp \
1415
c_misc.cpp \
1516
c_nondet_symbol_factory.cpp \
1617
c_object_factory_parameters.cpp \

src/ansi-c/c_expr.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*******************************************************************\
2+
3+
Module: API to expression classes that are internal to the C frontend
4+
5+
Author: Daniel Kroening, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#include "c_expr.h"
10+
11+
#include <util/arith_tools.h>
12+
13+
shuffle_vector_exprt::shuffle_vector_exprt(
14+
exprt vector1,
15+
optionalt<exprt> vector2,
16+
exprt::operandst indices)
17+
: multi_ary_exprt(
18+
ID_shuffle_vector,
19+
{std::move(vector1), nil_exprt{}, exprt{}},
20+
typet{})
21+
{
22+
if(vector2.has_value())
23+
op1() = std::move(*vector2);
24+
25+
const vector_typet &vt = to_vector_type(op0().type());
26+
type() =
27+
vector_typet{vt.subtype(), from_integer(indices.size(), vt.size().type())};
28+
29+
op2().operands().swap(indices);
30+
}
31+
32+
vector_exprt shuffle_vector_exprt::lower() const
33+
{
34+
PRECONDITION(
35+
!has_two_input_vectors() || vector1().type() == vector2().type());
36+
37+
if(indices().empty())
38+
return vector_exprt{exprt::operandst{}, type()};
39+
40+
auto input_size =
41+
numeric_cast<mp_integer>(to_vector_type(vector1().type()).size());
42+
CHECK_RETURN(input_size.has_value());
43+
44+
exprt::operandst operands;
45+
operands.reserve(indices().size());
46+
47+
for(const exprt &index : indices())
48+
{
49+
if(has_two_input_vectors())
50+
{
51+
operands.push_back(if_exprt{
52+
binary_predicate_exprt{
53+
index, ID_lt, from_integer(*input_size, index.type())},
54+
index_exprt{vector1(), index},
55+
index_exprt{
56+
vector2(),
57+
minus_exprt{index, from_integer(*input_size, index.type())}}});
58+
}
59+
else
60+
operands.push_back(index_exprt{vector1(), index});
61+
}
62+
63+
return vector_exprt{std::move(operands), type()};
64+
}

src/ansi-c/c_expr.h

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,97 @@ Author: Daniel Kroening, [email protected]
1414

1515
#include <util/std_code.h>
1616

17+
/// \brief Shuffle elements of one or two vectors, modelled after Clang's
18+
/// __builtin_shufflevector.
19+
class shuffle_vector_exprt : public multi_ary_exprt
20+
{
21+
public:
22+
shuffle_vector_exprt(
23+
exprt vector1,
24+
optionalt<exprt> vector2,
25+
exprt::operandst indices);
26+
27+
const vector_typet &type() const
28+
{
29+
return static_cast<const vector_typet &>(multi_ary_exprt::type());
30+
}
31+
32+
vector_typet &type()
33+
{
34+
return static_cast<vector_typet &>(multi_ary_exprt::type());
35+
}
36+
37+
const exprt &vector1() const
38+
{
39+
return op0();
40+
}
41+
42+
exprt &vector1()
43+
{
44+
return op0();
45+
}
46+
47+
const exprt &vector2() const
48+
{
49+
return op1();
50+
}
51+
52+
exprt &vector2()
53+
{
54+
return op1();
55+
}
56+
57+
bool has_two_input_vectors() const
58+
{
59+
return vector2().is_not_nil();
60+
}
61+
62+
const exprt::operandst &indices() const
63+
{
64+
return op2().operands();
65+
}
66+
67+
exprt::operandst &indices()
68+
{
69+
return op2().operands();
70+
}
71+
72+
vector_exprt lower() const;
73+
};
74+
75+
template <>
76+
inline bool can_cast_expr<shuffle_vector_exprt>(const exprt &base)
77+
{
78+
return base.id() == ID_shuffle_vector;
79+
}
80+
81+
inline void validate_expr(const shuffle_vector_exprt &value)
82+
{
83+
validate_operands(value, 3, "shuffle_vector must have three operands");
84+
}
85+
86+
/// \brief Cast an exprt to a \ref shuffle_vector_exprt
87+
///
88+
/// \a expr must be known to be \ref shuffle_vector_exprt.
89+
///
90+
/// \param expr: Source expression
91+
/// \return Object of type \ref shuffle_vector_exprt
92+
inline const shuffle_vector_exprt &to_shuffle_vector_expr(const exprt &expr)
93+
{
94+
PRECONDITION(expr.id() == ID_shuffle_vector);
95+
const shuffle_vector_exprt &ret =
96+
static_cast<const shuffle_vector_exprt &>(expr);
97+
validate_expr(ret);
98+
return ret;
99+
}
100+
101+
/// \copydoc to_shuffle_vector_expr(const exprt &)
102+
inline shuffle_vector_exprt &to_shuffle_vector_expr(exprt &expr)
103+
{
104+
PRECONDITION(expr.id() == ID_shuffle_vector);
105+
shuffle_vector_exprt &ret = static_cast<shuffle_vector_exprt &>(expr);
106+
validate_expr(ret);
107+
return ret;
108+
}
109+
17110
#endif // CPROVER_ANSI_C_C_EXPR_H

src/ansi-c/c_typecheck_base.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ class c_typecheck_baset:
203203
virtual code_blockt instantiate_gcc_polymorphic_builtin(
204204
const irep_idt &identifier,
205205
const symbol_exprt &function_symbol);
206+
virtual exprt
207+
typecheck_shuffle_vector(const side_effect_expr_function_callt &expr);
206208

207209
virtual void make_index_type(exprt &expr);
208210
virtual void make_constant(exprt &expr);

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1927,6 +1927,24 @@ void c_typecheck_baset::typecheck_side_effect_function_call(
19271927

19281928
return;
19291929
}
1930+
else if(
1931+
identifier == "__builtin_shuffle" &&
1932+
config.ansi_c.mode == configt::ansi_ct::flavourt::GCC)
1933+
{
1934+
exprt result = typecheck_shuffle_vector(expr);
1935+
expr.swap(result);
1936+
1937+
return;
1938+
}
1939+
else if(
1940+
identifier == "__builtin_shufflevector" &&
1941+
config.ansi_c.mode == configt::ansi_ct::flavourt::CLANG)
1942+
{
1943+
exprt result = typecheck_shuffle_vector(expr);
1944+
expr.swap(result);
1945+
1946+
return;
1947+
}
19301948
else if(
19311949
auto gcc_polymorphic = typecheck_gcc_polymorphic_builtin(
19321950
identifier, expr.arguments(), f_op.source_location()))

0 commit comments

Comments
 (0)