| Age | Commit message (Collapse) | Author | Files | Lines |
|
Add a testcase for "Newline in string or character constant" vs.
"missing delimitator" upcoming change.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Sparse unconditionally issues warnings about non-ANSI function
declarations & definitions.
However, some environments have large amounts of legacy headers
that are pre-ANSI, and can't easily be changed. These generate
a lot of useless warnings.
Fix this by using the options flags -Wstrict-prototypes &
-Wold-style-definition to conditionalize these warnings.
Signed-off-by: John Levon <levon@movementarian.org>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In legacy environment, a lot of warnings can be issued about
arguments without an explicit type.
Fix this by contitionalizing such warnings with the flag
-Wimplicit-int, reducing the level of noise in such environment.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In an old-style function definition, if not explicitly specified,
the type of an argument defaults to 'int'.
Sparse issues an error for such arguments and leaves the type
as 'incomplete'. This can then create a cascade of other warnings.
Fix this by effectively giving the type 'int' to such arguments.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Legacy code can be littered with the non-standard "#ident" directive;
ignore it.
Signed-off-by: John Levon <levon@movementarian.org>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently, when used on the kernel, sparse issues a bunch
of warnings like:
warning: constant 0x100000000 is so big it is long
These warning are issued when there is a discrepancy
between the type as indicated by the suffix (or the absence
of a suffix) and the real type as selected by the type
suffix *and* the value of the constant.
Since there is nothing incorrect with this discrepancy,
(no bits are lost) these warnings are more annoying than useful.
So, make them depending on a new warning flag -Wconstant-suffix
and make it off by default.
Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
On the cygwin platform, a 'sparsei' backend test, which uses the llvm
'lli' tool, fails due to a dynamic linking error:
$ make check
...
TEST sum from 1 to n (backend/sum.c)
error: actual output text does not match expected output text.
error: see backend/sum.c.output.* for further investigation.
--- backend/sum.c.output.expected 2018-06-03 18:27:11.502760500 +0100
+++ backend/sum.c.output.got 2018-06-03 18:27:11.307670000 +0100
@@ -1,2 +0,0 @@
-15
-5050
error: actual error text does not match expected error text.
error: see backend/sum.c.error.* for further investigation.
--- backend/sum.c.error.expected 2018-06-03 18:27:11.562997400 +0100
+++ backend/sum.c.error.got 2018-06-03 18:27:11.481038800 +0100
@@ -0,0 +1 @@
+LLVM ERROR: Program used external function 'printf' which could not be resolved!
error: Actual exit value does not match the expected one.
error: expected 0, got 1.
...
Out of 288 tests, 277 passed, 11 failed (10 of them are known to fail)
make: *** [Makefile:236: check] Error 1
$
Note the 'LLVM ERROR' about the 'printf' external function which could
not be resolved (linked). On Linux, it seems that the 'lli' tool (JIT
compiler) can resolve the 'printf' symbol, with the help of the dynamic
linker, since the tool itself is linked to the (dynamic) C library.
On windows (hence also on cygwin), the 'lli' tool fails to resolve the
external symbol, since it is not exported from the '.exe'.
The 'lli' tool can be used as an interpreter, so that the JIT compiler
is disabled, which also side-steps this external symbol linking problem.
Add the --[no-]jit options to the 'sparsei' tool, which in turn uses
(or not) the '-force-interpreter' option to 'lli'. In order to fix the
failing test-case, simply pass the '--no-jit' option to 'sparsei'.
Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The expression corresponding to the function pointer of indirect
call can be arbirarily complex. For example, it can contain a
statement expression or another call, possibly inlined.
These expressions must be expanded to insure that sub-expressions
involving 'sizeof()' or other operators taking a type as argument
(like __builtin_compatible_types_p()) are no more present (because
these expressions always evaluate to a compile-time constant and
so are not expected and thus not handled at linearization time).
However, this is not currently enforced, possibly causing some
failures during linearization with warnings like:
warning: unknown expression (4 0)
(which correspond to EXPR_TYPE).
Fix this, during the expansion of function calls, by also expanding
the corresponding designator.
References: https://lore.kernel.org/lkml/1542623503-3755-1-git-send-email-yamada.masahiro@socionext.com/
Reported-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
Tested-by: Masahiro Yamada <yamada.masahiro@socionext.com>
|
|
Add a testcase showing function designator are not expanded.
References: https://lore.kernel.org/lkml/1542623503-3755-1-git-send-email-yamada.masahi>
Reported-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
|
|
Currently, the error message issued for an empty enum is
"bad enum definition". This is exactly the same message
used when one of the enumerator is invalid.
Fix this by using a specific error message.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
GCC uses an unsigned type for enum's basetype unless one of
the enumerators is negative.
Using 'int' for plain simple enumerators and then using the same
rule as for integer constants (int -> unsigned int -> long -> ...)
should be more natural but doing so creates useless warnings when
using sparse on the kernel code.
So, do the same as GCC:
* uses the smaller type that fits all enumerators,
* uses at least int or unsigned int,
* uses an signed type only if one of the enumerators is negative.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Sparse supports enum initializers with bitwise types but
this makes sense only if they are all the same type.
Add a check and issue a warning if an enum is initialized
with different restricted types.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
As an extension to the standard C types, parse supports bitwise
types (also called 'restricted') which should in no circonstances
mix with other types.
In the kernel, some enums are defined with such bitwise types
as initializers; the goal being to have slightly more strict enums.
While the semantic of such enums is not very clear, using a mix
of bitwise and not-bitwise initializers completely defeats the
desired stricter typing.
Attract some attention to such mixed initialization by issuing
a single warning for each such declarations.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The C standard requires that the type of enum constants is 'int'
and let the enum base/compatible type be implementation defined.
For this base type, instead of 'int', GCC uses the smallest type
that can represent all the values of the enum (int, unsigned int,
long, ...)
Sparse has the same logic as GCC but if all the initializers
have the same type, this type is used instead.
This is a sensible choice but often gives differents
result than GCC.
To stay more compatible with GCC, always use the same logic
and thus only keep the common type as base type for restricted
types.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Sparse want that an enum's enumerators have all the same type.
This is done by first determining the common type and then
calling cast_enum_list() which use cast_value() on each member
to cast them to the common type.
However, cast_value() doesn't create a new expression and doesn't
change the ctype of the target: the target expression is supposed
to have already the right type and it's just the value that is
transfered from the source expression and size adjusted.
It's seems that in cast_enum_list() this has been overlooked
with the result that the value is correctly adjusted but keep
it's original type.
Fix this by updating, for each member, the desired type.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Add various testcases for checking enum's base & enumerator type.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Members of an enum should all have the same type but
isn't so currently.
Add a testcase for it and mark it as 'known-to-fail'.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Shifting by an amount greater or equal than the width
of the type is Undefined Behaviour. In the present case,
when type_is_ok() is called with a type as wide as an
ullong (64 bits here), the bounds are shifted by 64
which is UB and at execution (on x86) the value is simply
unchanged (since the shift is done with the amount modulo 63).
This, of course, doesn't give the expected result and
as consequence valid enums can have an invalid base type
(bad_ctype).
Fix this by doing the shift with a small helper which
return 0 if the amount is equal to the maximum width.
NB. Doing the shift in two steps could also be a solution,
as maybe some clever trick, but since this code is in
no way critical performance-wise, the solution here
has the merit to be very explicit.
Fixes: b598c1d75a9c455c85a894172329941300fcfb9f
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
type_is_ok(), used to calculate the base type of enums,
has a bug related to UB when doing a full width rshift.
Add a testcase for this.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
This patch prints the address space number when a warning
"cast removes address space of expression" is triggered.
This makes easier to discriminate in between different address
spaces.
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
During SSA conversion, it is checked what can be promoted
and what cannot. Obviously, ints, longs, pointers can be
promoted, enums and bitfields can too. Complication arise
with unions and structs. Currently union are only accepted
if they contains integers of the same size. For structs
its even more complicated because we want to convert
simple bitfields. What should be accepted is structs
containing either:
* a single scalar
* only bitfields and only if the total size is < long
However the test was slightly more strict than that:
it dodn't allowed a struct with a total size bigger
than a long. As consequence, on IP32, a struct containing
a single double wasn't promoted.
Fix this by moving the test about the total size and
only if some bitfield was present.
Reported-by: Ramsay Jones <ramsay@ramsayjones.plus.com>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The test mem2reg/init-local.c succeeds on 64-bit but fails
on 32-bit.
Duplicate the test, one with -m64 and the other with -m32
and mark this one as known-to-fail.
Reported-by: Ramsay Jones <ramsay@ramsayjones.plus.com>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The test optim/cse-size fials on 32-bit because it needs
two integers of different size but uses int & long.
These two types have indeed different sizes on 64-bit
(LP64) but not on 32-bit (ILP32).
Fix this by using short & int.
Reported-by: Ramsay Jones <ramsay@ramsayjones.plus.com>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The test Waddress-space-strict made assumptions about the
relative size of integers & pointers. Since this test was
crafted on a 64-bit machine, the test was running fine for
LP64 but failed on a 32-bit machine (or anything using IP32,
like using the -m32 option).
However, since the test is about conversion of address-spaces,
using integers of different size adds no value, and indeed
brings problems.
Fix this by limiting the conversions to a single integer type,
the one with the same size as pointers on ILP32 & LP64: long.
Reported-by: Ramsay Jones <ramsay@ramsayjones.plus.com>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* remove more complex phi-nodes
|
|
* fix linearization/SSA when missing a return
* fix linearization/SSA of (nested) logical expressions
|
|
The linearization of nested logical expressions is not correct
regarding the phi-nodes and their phi-sources. For example, code like:
extern int a(void); int b(void); int c(void);
static int foo(void)
{
return (a() && b()) && c();
}
gives (optimized) IR like:
foo:
phisrc.32 %phi1 <- $0
call.32 %r1 <- a
cbr %r1, .L4, .L3
.L4:
call.32 %r3 <- b
cbr %r3, .L2, .L3
.L2:
call.32 %r5 <- c
setne.32 %r7 <- %r5, $0
phisrc.32 %phi2 <- %r7
br .L3
.L3:
phi.32 %r8 <- %phi2, %phi1
ret.32 %r8
The problem can already be seen by the fact that the phi-node in L3
has 2 operands while L3 has 3 parents. There is no phi-value for L4.
The code is OK for non-nested logical expressions: linearize_cond_branch()
takes the sucess/failure BB as argument, generate the code for those
branches and there is a phi-node for each of them. However, with
nested logical expressions, one of the BB will be shared between
the inner and the outer expression. The phisrc will 'cover' one of
the BB but only one of them.
The solution is to add the phi-sources not before but after and add
one for each of the parent BB. This way, it can be guaranteed that
each parent BB has its phisrc, whatever the complexity of the sub-
expressions. With this change, the generated IR becomes:
foo:
call.32 %r2 <- a
phisrc.32 %phi1 <- $0
cbr %r2, .L4, .L3
.L4:
call.32 %r4 <- b
phisrc.32 %phi2 <- $0
cbr %r4, .L2, .L3
.L2:
call.32 %r6 <- c
setne.32 %r8 <- %r6, $0
phisrc.32 %phi3 <- %r8
br .L3
.L3:
phi.32 %r1 <- %phi1, %phi2, %phi3
ret.32 %r1
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Nested logical expressions are not correctly linearized.
Add a test for all possible combinations of 2 logical operators.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The linearization of logical '&&' create a phi-node with its
operands in the wrong order relatively to the parent BBs.
Switch the order of the operands for logical '&&'.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In valid SSA there is a 1-to-1 correspondance between
each operand of a phi-node and the parents BB.
However, currently, this is not always respected.
Add testcases for the known problems.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently, the code for the return is only generated if the
effectively return a type or a value with a size greater than 0.
But this mean that a non-void function with an error in its return
expression is considered as a void function for what the generated
IR is concerned, making things incoherent.
Fix this by using the declared type instead of the type of the
return expression.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
If a return statement is missing in the last block, the
generated IR will be invalid because the number of operands
in the exit phi-node will not match the number or parent BBs.
Detect this situation and insert an UNDEF for the missing value.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Top-level ASM statements are parsed as fake anonymous functions.
Obviously, they have few in common with functions (for example,
they don't have a return type) and mixing the two makes things
more complicated than needed (for example, to detect a top-level
ASM, we had to check that the corresponding symbol (name) had a
null ident).
Avoid potential problems by special casing them and return early
in linearize_fn(). As consequence, they now don't have anymore
an OP_ENTRY as first instructions and can be detected by testing
ep->entry.
Note: It would be more logical to catch them even erlier, in
linearize_symbol() but they also need an entrypoint and an
active BB so that we can generate the single statement.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In this case the phi-node created for the return value
ends up with a missing operand, violating the semantic
of the phi-node: map one value with each predecessor.
Add testcases for these missing returns.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
sparse issues a warning when user pointers are casted to integer
types except to unsigned longs which are explicitly allowed.
However it may happen that we would like to also be warned
on casts to unsigned long.
Fix this by adding a new warning flag: -Wcast-from-as (to mirrors
-Wcast-to-as) which extends -Waddress-space to all casts that
remove an address space attribute (without using __force).
References: https://lore.kernel.org/lkml/20180628102741.vk6vphfinlj3lvhv@armageddon.cambridge.arm.com/
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* fix linearization of unreachable switch + label
|
|
* add support for __has_attribute()
|
|
In a set of related phi-nodes and phi-sources if all phi-sources
but one correspond to the target of one of the phi-sources, then
no phi-nodes is needed and all %phis can be replaced by the unique
source.
For example, code like:
int test(void);
int foo(int a)
{
while (test())
a ^= 0;
return a;
}
used to produce an IR with a phi-node for 'a', like:
foo:
phisrc.32 %phi2(a) <- %arg1
br .L4
.L4:
phi.32 %r7(a) <- %phi2(a), %phi3(a)
call.32 %r1 <- test
cbr %r1, .L2, .L5
.L2:
phisrc.32 %phi3(a) <- %r7(a)
br .L4
.L5:
ret.32 %r7(a)
but since 'a ^= 0' is a no-op, the value of 'a' is in fact
never mofified. This can be seen in the phi-node where its
second operand (%phi3) is the same as its target (%r7). So
the only possible value for 'a' is the one from the first
operand, its initial value (%arg1).
Once this trivial phi-nodes is removed, the IR is the expected:
foo:
br .L4
.L4:
call.32 %r1 <- test
cbr %r1, .L4, .L5
.L5:
ret.32 %arg1
Removing these trivial phi-nodes will usually trigger other
simplifications, especially those concerning the CFG.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Trivial phi-nodes are phi-nodes having an unique possible outcome.
So, there is nothing to join and the phi-node target can be replaced
by the unique value.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
An unreachable/inactive switch statement is currently not
linearized. That's nice because it avoids to create useless
instructions.
However, the body of the statement can contain a label which
can be reachable. If so, the resulting IR will contain a branch
to an unexisting BB. Bad.
For example, code like:
int foo(int a)
{
goto label;
switch(a) {
default:
label:
break;
}
return 0;
}
(which is just a complicated way to write: int foo(int a) { return 0; })
is linearized as:
foo:
br .L1
Fix this by linearizing the statement even if not active.
Note: it seems that none of the other statements are discarded
if inactive. Good. OTOH, statement expressions can also
contains (reachable) labels and thus would need the same
fix (which will need much more work).
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
or more exactly, an unreachable switch statement but containing a
reachable label. This is valid code but is curently wrongly linearized.
So, add a testcase for it.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Sparse has support for a subset of GCC's large collection of
attributes. It's not easy to know which versions support this
or that attribute. However, since GCC5 there is a good solution
to this problem: the magic macro __has_attribute(<name>)
which evaluates to 1 if <name> is an attribute known to the
compiler and 0 otherwise.
Add support for this __has_attribute() macro by extending the
already existing support for __has_builtin().
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Add a testcase for the incoming support of __has_attribute().
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* fix: do not optimize away accesses to volatile bitfields
* support mode(__pointer__) and mode(__byte__)
|
|
Accesses to volatiles must, of course, not be optimized away.
For this, we need to check to type associated to the memory access.
Currently this is done by checking if the type of the result of
the memops is volatile or not. Usualy, the type of the result is
the same as the one of the access so everything is good but for
bitfields, the memop is not done with the type of the bitfield
itself but to its base type. Since this base type is unrelated
to the access type, it is generaly not marked as volatile even
when the access to the bitfield is volatile.
Fix this by using the true type of the access to set the field
struct instruction::is_volatile.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Accesses to bitfields must, of course, not be optimized away.
This is currently not the case.
Add a testcase for it.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* do 'classical' SSA conversion (via the iterated dominance frontier).
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
This testcase was added a bit too quickly in order to have
minimal testing of loop's linearization.
However, such test just comparing the raw output of test-linearize
is a big PITA because it's so sensible to things like pseudos' name
themselves depending very much on details about the linearization
and simplification. Also, this test didn't really tested anything,
it only allowed to track changes.
Remove it as it has no testing value.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* fix buggy recursion in kill_dead_stores()
* kill dead stores again after memops simplification is done.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Sparse can apply a mode on plain integer types.
Add a known-to-fail testcase showing the problem.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* simplify TRUNC((x & M') | y, N)
* simplify AND(SHIFT(a | b, S), M)
* simplify TRUNC(SHIFT(a | b, S), N)
|
|
The simplification of TRUNC(SHIFT(a | b, S), N) can be done by
combining the effective mask corresponding to TRUNC(_, N) with
the one corresponding to SHIFT(_, S).
This allows to also simplify signed bitfields. For example, code like:
struct s {
signed int :2;
signed int f:3;
};
int bfs(struct s s, int a)
{
s.f = a;
return s.f;
}
is now simplified into the minimal:
bfs:
trunc.3 %r4 <- (32) %arg2
sext.32 %r11 <- (3) %r4
ret.32 %r11
The simplification is done by calling simplify_mask_shift() with
the mask corresponding to TRUNC(_, N).
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The simplification of AND(SHIFT(a | b, S), M) can be done by combining
the mask M with the effective mask corresponding to SHIFT(_, S).
This instruction pattern is generated when accessing bitfields,
for example, code like:
struct u {
unsigned int :2;
unsigned int f:3;
};
int bfu(struct u s, int a)
{
s.f = a;
return s.f;
}
is now simplified into the minimal:
bfu:
and.32 %r11 <- %arg2, $7
ret.32 %r11
The simplification is done by introducing a small helper,
simplify_mask_shift(), doing the pattern matching and then calling
simplify_mask_shift_or() with the mask M.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
A N-bit truncate is not much different than ANDing with
a N-bit mask and so some simplifications done for AND can
also be done for TRUNC. For example for code like this:
char foo(int x, int y) { return (x & 0xffff) | y; }
the mask is unneeded and the function should be equivalent to:
char foo(int x, int y) { return x | y; }
The simplification in this patch does exactly this, giving:
foo:
or.32 %r4 <- %arg1, %arg2
trunc.8 %r5 <- (32) %r4
ret.8 %r5
while previously the mask was not optimized away:
foo:
and.32 %r2 <- %arg1, $0xffff
or.32 %r4 <- %r2, %arg2
trunc.8 %r5 <- (32) %r4
ret.8 %r5
This simplification is especially important for signed bitfields
because the TRUNC+ZEXT of unsigned bitfields is simplified into
an OP_AND but this is, of course, not the case for the TRUNC+SEXT
of signed bitfields.
Do the simplification by calling simplify_mask_or(), initialy used
for OP_AND, but with the effective mask corresponding to TRUNC(x, N):
$mask(N).
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
|
|
The instructions SHL(AND(x, M), S) can be simplified into SHL(x, S)
if (M << S) == (-1 << S).
For example, code like:
unsigned foo(unsigned x)
{
return (x & 0x000fffff) << 12;
}
is now optimized into:
foo:
shl.32 %r3 <- %arg1, $12
ret.32 %r3
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The instructions SHL(AND(x, M), S) can be simplified to 0
if (M << S) == 0.
For example code like:
unsigned foo(unsigned x)
{
return (x & 0xfff00000) << 12;
}
is now simplified into:
foo:
ret.32 $0
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The instructions LSR(AND(x, M), S) are already simplified into
AND(LSR(x, S), (M >> S)) but only if AND(x, M) has a single user.
However, if (M >> S) == (-1 >> S), the AND part is redundant and the
whole can always directly be simplified into LSR(x, S).
For example, code like:
unsigned foo(unsigned x)
{
unsigned t = (x & 0xfffff000);
return ((t >> 12) ^ (x >> 12)) & t;
}
is now optimized into:
foo:
ret.32 $0
because (t >> 12) is simplified into (x >> 12).
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The instructions LSR(AND(x, M), S) are already simplified into
AND(LSR(x, S), (M >> S)) but only if AND(x, M) has a single user.
However, if (M >> S) == 0, they can always directly be simplified to 0.
For example code like:
unsigned foo(unsigned x)
{
unsigned t = (x & 0x00000fff);
return (t >> 12) & t;
}
is now simplified into:
foo:
ret.32 $0
while previously it was:
foo:
and.32 %r2 <- %arg1, $0xfff
lsr.32 %r4 <- %r2, $12
and.32 %r6 <- %r4, %r2
ret.32 %r6
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The pattern LSR(AND(x, M), S) is already generically simplified into
((x >> S) & (M >> S)) but only if the sub-expression AND(x, M) is not
shared with some other expressions because the simplification modify it.
But for some special cases the expression can be simplified even if
the sub-expression is shared because the simplification doesn't need
to modify this AND(x, M) part.
Add the testcases for LSR and the incoming SHL.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The same simplifications done for LSR can be done for SHL
(with the appropriate mask).
For example, code like:
int foo(int a, int b)
{
return ((a & 0xfff00000) | b) << 12;
}
is now optimized into:
foo:
shl.32 %r5 <- %arg2, $12
ret.32 %r5
while previously it was:
foo:
and.32 %r2 <- %arg1, $0xfff00000
or.32 %r4 <- %r2, %arg2
shl.32 %r5 <- %r4, $12
ret.32 %r5
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
If the effective mask (M) corresponding to OP(_, K) is different
than the combined mask (C & M), then the inner mask (C) can be
replaced by the smaller (C & M), giving:
OP((x | M'), K) with M' == (C & M).
For example, code like:
int foo(int x)
{
return (x | 0xfffffff0) & 0xfff;
}
is now simplified into:
foo:
or.32 %r2 <- %arg1, $0xff0
and.32 %r3 <- %r2, $0xfff
ret.32 %r3
while previously, the constant was not reduced:
foo:
or.32 %r2 <- %arg1, $0xfffffff0
and.32 %r3 <- %r2, $0xfff
ret.32 %r3
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In an expression like OP((x | C), K), if the effective mask (M)
corresponding to OP(_, K) is equal to the combined mask (C & M),
then the OR operation is unneeded and can be replaced by M itself,
giving: OP(M, K).
In mathematical terms:
0) ((x | C) & M) = ((x & M) | (C & M))
1) (C & M) = M
2) ((x & M) | (C & M)) = ((x & M) | M) = M
and so OP((x | C), K) -> OP(M, K).
For example, code like:
unsigned int foo(int x)
{
return (x | 7) & 2;
}
is now simplified into:
foo:
ret.32 $2
which previously was not optimized
foo:
or.32 %r2 <- %arg1, $7
and.32 %r3 <- %r2, $2
ret.32 %r3
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In an expression like OP((x | C), K), if the inner constant
(C) and the effective mask (M) corresponding to OP(_, K) are
disjoint ((C & M) == 0), then the OR with the constant is
unneeded and can be optimized away since the constant will be
'cleared' by the mask, giving: OP(x, K)
For example, code like:
int foo(int x)
{
return (x | 0xfffff000) & 0xfff;
}
is now optimized into:
foo:
and.32 %r3 <- %arg1, $0xfff
ret.32 %r3
while previously the OR mask was not optimized away:
foo:
or.32 %r2 <- %arg1, $0xfffff000
and.32 %r3 <- %r2, $0xfff
ret.32 %r3
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently, in simplify_mask_or_and(), only the cases where
(M' & M) == 0 or (M' & M) == M are simplified. However, if the
combined mask (M' & M) is different than the original inner mask
(M'), this inner mask can be replaced by the smaller combined one,
giving: OP(((x & (M' & M)) | y), K).
For example, code like:
int foo(int x, int y)
{
return (((x & 0xfffffff0) | y) & 0xfff);
}
is now simplified into:
foo:
and.32 %r2 <- %arg1, $0xff0
or.32 %r4 <- %r2, %arg2
and.32 %r5 <- %r4, $0xfff
ret.32 %r5
while previously, the mask was not reduced:
foo:
and.32 %r2 <- %arg1, $0xfffffff0
...
Note: this is not a very effective simplification like directly
removing an instruction, nevertheless the smaller mask can
trigger other simplifications and may also be advantageous
for a subsequent code generation phase.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In an expression like this, if the inner mask (M') 'covers' all the
bits of the effective mask (M) corresponding to OP(_, K), IOW if
(M' & M) == M, then the inner mask (M') is unneeded and can be
optimized away, giving: OP((x | y), K).
For example, with this simplification, code like:
int foo(int x, int y)
{
return (((x & 0x0fffffff) | y) & 0xfff);
}
is now optimized into the minimal:
foo:
or.32 %r4 <- %arg1, %arg2
and.32 %r5 <- %r4, $0xfff
ret.32 %r5
while previously, the inner mask was not optimized away:
foo:
and.32 %r2 <- %arg1, $0xfffffff
or.32 %r4 <- %r2, %arg2
and.32 %r5 <- %r4, $0xfff
ret.32 %r5
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In simplify_mask_or(), two calls to simplify_mask_or_and() are
made: one for each operand of the OR instruction.
These two calls are guarded by a test checking the presence of
the AND instruction. If it is the case for the first operand,
the second operand is never considered for simplification,
even if no simplifications have been made.
Fix this by testing the return value of simplify_and_or_mask()
and let's do the second call if no simplifications could be
done in the first one.
For example, code like:
int foo(int x, int y, int a)
{
return ((a & 0xf000) | (x & y)) & 0x0fff;
}
was expectedly simplified into:
foo:
and.32 %r5 <- %arg1, %arg2
and.32 %r7 <- %r5, $0xfff
ret.32 %r7
while the same code with the operands of the OR swapped was not:
int foo(int x, int y, int a)
{
return ((x & y) | (a & 0xf000)) & 0x0fff;
}
resulted in the non-optimized:
foo:
and.32 %r3 <- %arg1, %arg2
and.32 %r5 <- %arg3, $0xf000
or.32 %r6 <- %r3, %r5
and.32 %r7 <- %r6, $0xfff
ret.32 %r7
but now the simplification can also be done:
foo:
and.32 %r3 <- %arg1, %arg2
and.32 %r7 <- %r3, $0xfff
ret.32 %r7
Note: it would be simpler to unconditionally do both calls
but this is unsafe because some of the concerned
instructions, needed in the second call, could have
been simplified away in the first one.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The extraction & insertion of bitfields is made of relatively
complex combinations of SHL/LSR/AND/OR and TRUNC/ZEXT/SEXT.
Add a few testcases showing the effectiveness of their
simplification and to catch possible future regressions.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
There is a potential problem when the second side of the OR
is simplified away.
Add 2 testcases to catch possible regressions here.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* add simplification of TRUNC(TRUNC((x))
* add simplification of SHL(LSR(x), S), S)
|
|
The simplification of ZEXT(ZEXT(x)) was already added but
its dual, TRUNC(TRUNC(x)), was not.
Add it now.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The simplification of ((x << S) >> S) was already added
but its dual was forgotten.
Add it now.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Add the testcase before doing the simplification.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Add the testcase before doing the simplification.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The testcase was named with the wrong operation order
(inner op first instead of outer-first).
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* generate integer-wide OP_ADD & OP_SUB in linearize_inc_dec()
* simplify mask instructions & bitfield accesses
|
|
This is especially usefull when simplifying code
accessing bitfields.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
This transformation is especially usefull when simplifying code
accessing bitfields or for other masking manipulations.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
This is a common pattern for bitfield simplification.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
If the ++ or -- operator is used on a bitfield, the addition or
subtraction is done with the size of the bitfield. So code like:
struct {
int f:3;
} s;
...
s->f++;
will generate intermediate code like:
add.3 %r <- %a, $1
This is not incorrect from the IR point of view but CPUs have
only register-sized instructions, like 'add.32'. So, these
odd-sized instruction have one or two implicit masking/extend
that should better make explicit.
Fix this by casting to and from the base type when these operators
are used on bitfields.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The mask used for bitfield insertion is as big as the integers
used internally by sparse. Elsewhere in the code, constants are
always truncated to the size of the instructions using them.
It's also displaying concerned instructions oddly. For example:
and.32 %r2 <- %r1, 0xfffffffffffffff0
Fix this by limiting the mask to the size of the instruction.
Fixes: a8e1df573 ("bitfield: extract linearize_bitfield_insert()")
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The function cast_pseudo() can issues warnings about non size-preserving
integer <-> pointer casts. The file:line:column position of these warnings
is taken from the destination type but this position is the one where the
type is declared (or where the symbol associated with this type is defined)
which may or may not be related to the position of the cast.
Fix this by using the current position instead (which should hold the
position of the cast).
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
If an expression is already a boolean (constant) expression,
converting it to a boolean expression is a no-op.
In this case, return early, this avoids to create intermediate
code that will need to be simplified away at some later stage.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Since the OP_SETCC instructions can only return a 0 or a 1,
a subsequent AND mask can often be heavily simplified.
Simplify the mask value.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Since the OP_SETCC instructions can only return a 0 or a 1,
a truncation won't change the value and the OP_SETCC
can be changed to directly return the extended size.
Remove the unneeded truncate.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Since the OP_SETCC instructions can only return a 0 or a 1, a
sign-extension won't change the value if the size is wider than 1.
The OP_SETCC can thus be changed to directly return the extended size.
Remove the unneeded extension.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Since the OP_SETCC instructions can only return a 0 or a 1,
a zero-extension won't change the value and the OP_SETCC
can be changed to directly return the extended size.
Remove the unneeded extension.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Convert the OP_TRUNC into an OP_AND with the appropriate mask.
This a greater chance to simplify with previous instructions
and to correspond to a register-size operand.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Since the OP_SETCC instructions can only return a 0 or a 1,
a compare-not-equal-zero or compare-equal-1 is a no-op
if the operand have been masked with 1.
Remove the no-op comparison (if the size matches).
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The linearized code for logical expressions looks like:
.Lc
... condition 1 ...
cbr %c, .L1, .L2
.L1
%phisrc %phi1 <- $1
br .Lm
.L2
... condition 2 ...
%phisrc %phi2 <- %r
br .Lm
.Lm
%phi %r <- %phi1, %phi2
But .L1 can easily be merged with .Lc:
.Lc
... condition 1 ...
%phisrc %phi1 <- $1
cbr %c, .Lm, .L2
.L2
... condition 2 ...
%phisrc %phi2 <- %r
br .Lm
.Lm
%phi %r <- %phi1, %phi2
Do this simplification which:
* creates less basic blocks & branches
* do at linearization time a simplification not done later.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
linearize_logical() call linearize_conditional() but needs additional
tests there and generate code more complicated than needed.
Change this by expanding the call to linearize_conditional() and make
the obvious simplification concerning the shortcut expressions 0 & 1.
Also, removes the logical-specific parts in linearize_conditional(),
since there are now unneeded.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The function linearize_conditional(), normaly used for conditionals
(c ? a : b) is also used to linearize the logical ops || and &&.
For conditionals, the type evaluation ensure that both LHS & RHS
have consistent types. However, this is not the case when used for
logical ops. This creates 2 separated but related problems:
* the operands are not compared with 0 as required by the standard
(6.5.13, 6.5.14).
* both operands can have different, incompatible types and thus
it's possible to have a phi-node with sources of different,
incompatible types, which doesn't make sense.
Fix this by:
* add a flag to linearize_conditional() telling if it's used for
a conditional or for a logical op.
* when used for logical ops:
* first compare the operands againts zero
* convert the boolean result to the expression's type.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Conditional branches, or more exactly OP_CBR, can't accept
arbitrary expression as condition. it is required to have
an integer value.
Fix this by adding a comparison against zero.
|
|
Add some tests in preparation of some bug-fixing and
simplification in linearize_logical()linearize_conditional().
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* some simplifications of OP_SETNE & OP_SETEQ with 0 and 1
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
These two comparisons are no-ops when the operand is boolean.
Simplify them away.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
A sign-extension has no effect on the result of a comparison
with 0 or with 1 when the original size is bigger than 1.
Optimize away the extension.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
A zero-extension has no effect on the result of a comparison with 0 or 1.
Optimize away the extension.
Note: this simplification was already done but only when the
original size was 1 but it can be done for all sizes.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Add some basic testcase for these relatively common
simplification opportunities.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* several simplifications involving casts and/or bitfields
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* give a correct & sensible warning on negative or over-sized shifts.
* add conservative simplification of such shifts.
* do not optimize the meaningless shift:
* any shift with a negative count
* OP_ASRs with an over-sized shift count.
* try to give a correct negative/too-big error message during simplification.
* simplify chains of shifts.
* simplify ZEXT + ASR into ZEXT + LSR
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
After an OP_ZEXT, it is guaranteed that the sign bit is zero.
So, an arithmetic right-shift following an OP_ZEXT gives the
same result as an logical right-shift which is simpler to handle
and further optimize.
So, when preceded by an OP_ZEXT, replace OP_ASR by OP_LSR.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Since an LSR with an in-range shift count will insert a zero in
the MSB, a subsequent ASR will be equivalent to an LSR of the same
count or equivalently, the combinaison the these two shifts is
equivalent to a single LSR with a shift count equal to the sum
of the two initial counts.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
A shift of a shift (of the same kind) is equivalent to a single
shitf with a count equal to the sum of the two initial counts.
In addition:
* for LSRs & SHLs, if the sum is >= to the instruction size, then
the result is zero.
* for ASRs, if the sum is >= to the instruction size, then
the result is the same as a shift of a count of size - 1.
Implement these simplifications if both shift counts are in range.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The argument of the typeof operator is normally not evaluated
but it should be when it involves a VLA.
Add a testcase for this.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Apparently, only loads are detected, stores are not.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Floating-point values are displayed using the printf
format "%Lf" but this is the format without exponent
(and with default precision of 6 digit).
However, by its nature, this format is very imprecise.
For example, *all* values smaller than 0.5e-6 are displayed
as "0.000000".
Improve this by using the "%Le" format which always use
an exponent and thus maximize the precision.
Note: ultimately, we should display them exactly, for
example by using "%La", but this will requires C99.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In order to preserve shifts with negative count and give a proper
warning for them, we first need to identify when the shift must
be considered as negative. This identification is made complicated
by the fact that PSEUDO_VAL are typeless.
The solution chosen here is to try to first sign extend the shift
count (with the size of an int and the size of the instruction).
If after this extension the count appears as negative it is treated
as such (and this new value replace the old one to avoid to have to
redo this at each simplification run), otherwise it is considered
as unsigned. The only case where a negative count could be wrongly
interpreted as a big unsigned value would be if:
size target int < instruction's size < size host value
which can't happen for now since sizeof(int) == 4 on all targets
and sizeof(value) is 8.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Like GCC, we don't want to touch shifts with a negative count.
Add testcases covering this at expansion and simplification time.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Like GCC, we don't want to touch over-sized ASR but over-sized
LSRs & SHLs degenerate to 0.
Add testcases covering all cases.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
A sign-extension following a zero-extension can always
be simplified into a single zero-extension since the
intermediate value is guaranted to have a zero sign bit.
Simplify away such instructions.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
A zero-extension following another zero-extension can always
be simplified into a single zero-extension.
Simplify away such instructions.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
A sign-extension following another sign-extension can always
be simplified into a single sign-extension.
Simplify away such instructions.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
An OP_AND with a constant value following a OP_ZEXT can often
be simplified:
* the constant mask can be made smaller
* the whole masking is redundant and can be removed.
Do these two simplifications depending on the initial mask
and the sizes of the instructions.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
A OP_AND with a constant value followed by a sign or zero-extension
can often be moved after the extension.
This is good because it can trigger the cancellation of the OP[ZS]EXT
with a preceding OP_TRUNC, a common situation with bitfields.
Note: This 'simplification' is less desirable if there is
no preceding OP_TRUNC, for example, in a sequence like:
and.32 %t <- %a, $mask
*ext.64 %r <- %t
If needed, this can be filtered out at some later stage.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
sparse contains some code to simplify an AND masking
followed by a truncating cast.
However, this simplification is problematic because it
doesn't keep the sizes consistent. For example, code like:
and.32 %r3 <- %r2, $0x7fff
trunc.16 %r4 <- (32) %r3
will be discarded with %r3 used in place of %r4.
This is correct in the mathematical sense but %r4 had a size
of 16 while %r3 has a size of 32, so using %r3 in place of %r4
will make the sizes inconsistent with unexpected consequences.
We can more or less fix this by using another transformation
that preserve the sizes:
trunc.16 %r3 <- (32) %r2
and.16 %r4 <- %r3, $0x7fff
which in itself doesn't optimize anything but:
1) the mask may be smaller
2) may trigger other simplification with the TRUNC or the AND.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
An OP_SEXT or a OP_ZEXT followed by a truncate to a size smaller
than the original size is unneeded, the same result can be obtained
by doing the truncate directly on the original value.
Dualy, an OP_SEXT or a OP_ZEXT followed by a truncate to a size greater
than the original size doesn't need the truncate, the same result can be
obtained by doing the extend directly on the original value.
Rearrange the inputs (src & orig_type) to bypass the unneeded operation.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
An OP_ZEXT/OP_SEXT following by a OP_TRUNC to the original size is a NOP.
Simplify away such OP_TRUNC.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
There is several difficulties some related to unclear semantic
of our IR instructions and/or type evaluation.
Add testcases trying to cover this area.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The function check_shift_count() is used to check the validity
of shift counts but the count is truncated to an (host) int.
This truncated value can thus miss very large (or very negative)
shift count.
Fix this by using the full width shift count when doing the check.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The diagnostic emitted when shifting by an negative amount is confusing:
warning: shift too big (4294967295) for type ...
Change the message to the more informative:
warning: shift count is negative (-1)
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently, during simplification, when it's checked if the shift
amount of a shift instruction is in range, it's not the
instruction's size (which is the same as the type's width)
that is used but the 'operand size' as returned by operand_size().
operand_size() is a bit like poor man's Value Range Propagation
or VRP without Propagation, and use the knowledge of previous
instructions to deduce the effective size of the operand.
For example, if the operand is the result of a narrowing cast
or have been ANDed with a known mask, or was a bitfield, the
size returned is the one of the cast or the mask and may be
smaller than its type.
A priori, using more precise knowledge is a good thing but in
the present case it's not because it causes warning for things
that are totally legal, meaningfull and used all the time.
For example, we don't want to warn in the following code:
struct bf { int u:8; };
int bf(struct bf *p) { return p->s << 24; }
Another situation where such warnings are not desirable is
when the shift instruction 'is far' from the one defining
the size, for example when the shift occurs in an inline function
and this inline function is called with a smaller type.
So, use the instruction size instead of the effective operand size
when checking the range of a shift instruction.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In the mathematical sense, the result of a left-shift by an
amount bigger than the operand size equals zero.
Do the corresponding simplification.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In the mathematical sense, the result of LSR by an amount bigger
than the operand size equals zero.
Do the corresponding simplification.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
During the simplification phase, ASR instructions are checked
and possibly simplified but LSR & SHL are not.
Rename simplify_asr() into simplify_shift() and do the check
& simplification for LSR & SHL too.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently, arithmetic right shifts with a shift count bigger
than the operand size are simplified to zero.
While this makes a lot of sense for LSR and SHL, for ASR it's
much less so.
Remove the simplification and let the back-end generate the code
for a non-immediate count (which is what GCC do).
Note: Some other options would be:
- reduce the shift count modulo the operand size, like it was
done previously at expansion time and which correspond to the
run-time behaviour of several CPUs families (x86[-64], arm64,
mips, ...) but not all of them (arm, ppc, ...).
- truncate the count to N-1
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
At expansion time, a diagnostic is emitted for shift with a
bad count in a shift expression but not in a shift-assign.
Fix this by calling the checking function also for shift-assigns.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The following patches change quite a bit of things regarding
shifts with bad count. Add testcases to ensure everything is
covered and catch possible future regressions.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
This patch optimize the very simple implementation of the
phi-node renaming at the end of the SSA conversion.
It avoids the need to rescan the whole function to find the phi-nodes
by using a worklist to put the phi-nodes during the renaming
of non-phi nodes instructions.
This optimization avoids O(n^2) behaviour in some pathological cases.
Note: A lot of optimizations can be done for the renaming.
For the moment, things are kept as simplest as possible,
the goal being to have correctness first.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
This activate the new SSA conversion that will be used
to replace simplify_symbol_usage() which created invalid
SSA (phi-nodes were placed where the value was needed
instead of where the paths meet, also and partially related,
it was possible for a phi-node to have more operands/sources
than the BB it was in had parents).
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
This testcase was added a bit too quickly in order to have
minimal testing of loop's linearization.
However, such test just comparing the raw output of test-linearize
is a big PITA because it's so sensible to things like pseudos' name
themselves depending very much on details about the linearization
and simplification. Also, this test didn't really tested anything,
it only allowed to track changes.
Remove it as it has no testing value.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
A few tests are added, some have been renamed to better
refect their purposes. Finally, some checks have been added
or tweaked.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Split the existing test in 2 as it contains 2 different cases.
Also move the test to 'linear/' subdir.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In kill_dead_stores_bb(), after having scanned the current BB,
we may recurse into the parent BBs. But we must do this only if
we're sure that the store there may not be needed in another BB.
In other words, we must do this only if the current BB is the only
child of the parent.
However, if one of the parent has more than one child, instead
of trying the next parent, the function stops there and returns.
Furthermore, the check is done with an open loop instead of using
the helper bb_list_size().
Fix this by using bb_list_size() to check if the parent has only
one child, do the recursion if it is the case and try the next
parent if not.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently, dead stores are removed after the initial
promotion of symbols to pseudos (simplify_one_symbol()).
But more complex promotions are done later during memops
simplification (simplify_loads()) and dead stores should be
removed there too but aren't.
Fix this by calling kill_dead_stores() after memops
simplification.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* cse: try the next pair instead of keeping the first instruction
* cse: compare casts only by kind a size, not by C type.
|
|
Currently OP_SWITCHes are only handled by default in kill_insn().
In consequence, when killed, OP_SWITCHes leave a fake usage on the
condition.
Fix this by removing the condition's usage when killing an OP_SWITCH.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* optimize away OP_UTPTR & OP_PTRTU which are nops.
|
|
Now that all casts to or from a pointer are between a pointer
and a pointer-sized unsigned integer, from an optimization
PoV, they are all no-ops.
So, optimize them away at simplification time.
Note: casts between pointers (OP_PTRCAST) should also be
optimized away but the original type is used for a
number a things (for example in check_access()) and
can't be optimized away so simply (yet).
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
validation/linear/* should not contain testcases that are
optimization dependent and validation/*.c should not contain
tests using 'test-linearize', only those using 'sparse'.
Move some cast-related testcases accordingly.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Now that OP_AND_BOOL and OP_OR_BOOL are always given boolean
operands, they are just a special case of 1 bit OP_AND & OP_OR.
To avoid to have to repeat CSE, simplification patterns, ...
better to generate plain OP_AND & OP_OR instead.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The function add_convert_to_bool() was added to give a
boolean context to logical expressions but did this onl
for integers.
Fix this for floating-point expressions by adding the proper
comparison to 0.0.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Because of C's integer promotion, in code like 'a == 0',
the operand 'a' must be promoted to int. So, if 'a' is
of type 'bool', it results in following linearization:
zext.32 %t <- (1) %a
setne.32 %r <- %t, $0
While this promotion is required by the standard at C level,
here, from an operational PoV, the zero-extension is unneeded
since the result will be the same without it.
Change this by simplifying away such zero-extensions.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
This is yet another simple identity with the potential to trigger
more simplifications.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
This is another simple identity with the potential to trigger
more simplifications.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
This is a simple identity with the potential to trigger
more simplifications.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The current SSA conversion kinda ignore undefined variables.
Those may then later be detected when they are part of a
LOAD + (ADD|SUB) cycle but they can also create other cycles
which are not detected.
These cycles inhibit (or uselessly complicate) lots of optimizations.
For example, code like:
and.32 %r2 <- %r1, $1
and.32 %r3 <- %r2, $1
should be simplified into:
and.32 %r3 <- %r1, $1
but this simplification would behave horribly with 'cycles' like:
and.32 %r1 <- %r1, $1
This patch add a testcase for a number a very simple situations
where such cycles can be created.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
A truncation followed by a zero-extension to the original size,
which is produced when loading a storing bitfields, is equivalent
to a simple AND masking. Often, this AND can then trigger even
more optimizations.
So, replace TRUNC + ZEXT instructions by the equivalent AND.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Let's suppose we have a few instructions: A, B, C & D,
all congruent insn_compare()
The current way the CSE is done is:
The first try will be try_to_cse(A,B). If this succeeds,
A & B have been merged (AB) and the next try will be done
with the next instruction:
try_to_cse(AB,C).
That's good. However, if it fails, the next try will be:
try_to_cse(A,C).
If this one also fails, the next one will be:
try_to_cse(A,D)
And if this one also fails, nothing else is done.
In other words, all attempts are done with A. If it happens that
A can't be eliminated (because it doesn't dominate B, C & D and
is not dominated by them), no eliminations are done because the
other pairs, not involving A are never tried.
Ideally, we should try all possible pairs: A-B, A-C & A-D but
also B-C, B-D & C-D. However this is quadratic and can be a bit
costly. An easy & cheap way to improve the current situation is,
in case of failure, to not reuse the original first instruction
but the other one. So instead of testing:
A-B, A-C, A-D, ...
we will test:
A-B, B-C, C-D, ...
with the result that an 'bad' instruction can't block anymore
the following pairs.
So, when try_to_cse() fails, do not retry by keeping the first
instructions, but retry using the second one.
In practice, this seems to work fairly well.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The last instruction of linearize_load_gen() ensure that
loading a bitfield of size N results in a object of size N.
Also, we require that the usual binops & unops use the same type
on their operand and result. This means that before anything
can be done on the loaded bitfield it must first be sign or zero-
extended in order to match the other operand's size.
The same situation exists when storing a bitfield but there
the extension isn't done. We can thus have some weird code like:
trunc.9 %r2 <- (32) %r1
shl.32 %r3 <- %r2, ...
where a bitfield of size 9 is mixed with a 32 bit shift.
Avoid such mixing of size and always zero extend the bitfield
before storing it (since this was the implicitly desired semantic).
The combination TRUNC + ZEXT can then be optimised later into
a simple masking operation.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
In the test the offset is the same for dst & src and thus
it's calculation should be CSEed away but it is not (yet).
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Casts to integer used to be done with only 2 instructions:
OP_CAST & OP_SCAST.
Those are not very convenient as they don't reflect the real
operations that need to be done.
This patch specialize these instructions in:
- OP_TRUNC, for casts to a smaller type
- OP_ZEXT, for casts that need a zero extension
- OP_SEXT, for casts that need a sign extension
- Integer-to-integer casts of the same size are considered as
a NOPs and are, in fact, never emitted.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently casts from pointers can be done to any integer type.
However, casts to (or from) pointers are only meaningful if
it preserves the value and thus done between same-sized objects.
To avoid to have to worry about sign/zero extension while doing
casts to pointers it's good to not have to deal with such casts.
Do this by doing first a cast to an unsigned integer of the same size
as a pointer and then, if needed, doing to cast to the final type.
As such we have only to support pointer casts to unsigned integers
of the same size and on the other hand we have the generic
integer-to-interger casts we to support anyway.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
It's relatively common to cast a pointer to an unsigned long,
for example to make some bit operations.
It's much less sensical to cast a pointer to an integer smaller
(or bigger) than a pointer is.
So, emit a diagnostic for this, under the control of a new
warning flag: -Wpointer-to-int-cast.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently all casts to pointers are processed alike. This is
simple but rather unconvenient in later phases as this
correspond to different operations that obeys to different
rules and which later need extra checks.
Change this by using a specific instructions (OP_UTPTR) for
[unsigned] integer to pointers.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently casts to pointers can be done from any integer types.
However, casts to (or from) pointers are only meaningful if value
preserving and thus between objects of the same size.
To avoid to have to worry about sign/zero extension while doing
casts to pointers it's good to only have to deal with the value
preserving ones.
Do this by doing first, if needed, a cast an integer of the same
size as a pointer before doing the cast to a pointer.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently all casts to pointers are processed alike.
This is simple but rather unconvenient as it correspond to
different operations that obeys to different rules and
which later need extra checks.
Change this by using a specific instructions (OP_UTPTR) for
unsigned integer to pointers.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently, casts from floats to integers are processed like
integers (or any other type) to integers. This is simple but
rather unconvenient as it correspond to different operations
that obeys to different rules and which later need extra checks.
Change this by directly using specific instructions:
- FCVTU for floats to unsigned integers
- FCVTS for floats to signed integers
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Some casts, the ones which doesn't change the size or the resulting
'machine type', are no-op.
Directly simplify away such casts.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently, all casts to a floating point type use OP_FPCAST.
This is maybe simple but rather uncovenient as it correspond
to several quite different operations that later need extra
checks.
Change this by directly using different instructions for the
different cases:
- FCVTF for float-float conversions
- UCVTF for unsigned integer to floats
- SCVTF for signed integer to floats
and reject attempts to cast a pointer to a float.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The sparse command (aka the 'checker') do a number of additional
checks when used with the -v flag (I strongly believes that this
option is rarely used but let me not disgress about it here).
One of these additional checks is about casts.
Add some testcases in order to catch any problems here.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
There are special problems when a typeof() expression can't
be evaluated. Catch this here.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
* merge the tests about implicit & explicit casts in a
single file as there was a lot of redundancy.
* shuffle the tests to linear/ or optim/
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
A integer-to-float cast of a constant is currently simplified away
as if it is an integer-to-integer cast. That's bad.
Fix this by refusing to simplify away any integer-to-float casts
like already done for float-to-integer casts.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
A integer-to-float cast of a constant is currently simplified away
as if it is an integer-to-integer cast. That's bad.
Create a testcase for it.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
This allow, for example, to simulate running the testsuite on a 32bit
machine or running the testsuite with some extra debugging flags.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Some non-void functions in the testcases miss a return.
Add the missing return or make the function as returning void.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
When using sparse it's common to compile a file and directly
run sparse on the same file, like it is done for the kernel.
In this case, error messages from sparse are interspersed with
those from the compiler. It's thus not always easy to know from
which tools they come.
Fix this by allowing to prefix all the diagnostic messages
by some configurable string, by default "sparse". More exactly,
an error message that was emitted like:
file.c:<line>:<col>: error: this is invalid code
can now be emitted as:
file.c:<line>:<col>: sparse: error: this is invalid code
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
Reviewed-by: Ramsay Jones <ramsay@ramsayjones.plus.com>
|
|
This will merge the following topic branches:
* builtin-dyn:
Expansion of macros like __FILE__, __DATE__, ... must, contrary
to usual macros, be done dynamically.
This series improves support for them:
- consider them as defined (like GCC do)
- use table + method for their expansion (avoid useless compares)
- add support for __INCLUDE_LEVEL__ & __BASE_FILE__
* builtin-predef:
Add a function: predefine() which allow to directly add
the definition of a simple macro (without args and with
a single number or ident as definition).
Also do the conversion of the concerned predefined macros
and some cleanups.
* builtin-overflow:
Add support for builtins doing overflow checking.
* has-builtin:
Add support for the __has_builtin() macro.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Sparse has support for a subset of GCC's large collection of builtin
functions. As for GCC, it's not easy to know which builtins are
supported in which versions.
clang has a good solution to this problem: it adds the checking macro
__has_builtin(<name>) which evaluates to 1 if <name> is a builtin
function supported by the compiler and 0 otherwise.
It can be used like:
#if __has_builtin(__builtin_clz)
#define clz(x) __builtin_clz(x)
#else
...
#endif
It's possible or probable that GCC will have this soon too:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66970
Add support for this __has_builtin() macro by extending
the evaluation of preprocessor expressions very much like
it is done to support defined().
Note: Some function-like builtin features, like __builtin_offset(), are
considered as a kind of keyword/operator and processed as such.
These are *not* considered as builtins by __has_builtin().
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
It seems they will be used in the kernel so add them.
Unlike __builtin_uadd_overflow() and friends, these don't take
a fixed type and thus can't simply be declared but need their
own evaluate() method to do the type checking.
Note: of course, no expansion is done on them.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Add a few silly testcases doing some expansion of a builtin macro.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
There was some support for it but it was just a define
that expanded to the fixed name "base_file.c".
Implement the real thing by saving the input filename
and replacing __BASE_FILE__ by it.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
This macro, which is supported by GCC, wasn't yet by sparse.
Add support for it.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
GCC consider these macros as being defined, sparse doesn't.
Also sparse uses a sequence of comparisons, one for each of
__DATE__, __FILE__, ..., which is not ideal.
Fix this by defining and using a table to associate to the
corresponding symbol a method doing the expansion.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
GCC considers __LINE__, __FILE__, ... as being defined.
Add a testcase for this.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
'fix-builtin-expect' and 'int-const-expr' into tip
|
|
This use Martin's awesome macro to test if sparse's
notion of integer-const-expr is the same as GCC's.
It test also that the result of this macro is itself a
constant integer expression.
Awesome-macro-by: Martin Uecker <Martin.Uecker@med.uni-goettingen.de>
Test-originally-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
If an error occurs at the end of the input, for example because
a missing terminating ';' or '}', the error message is like:
builtin:0:0: error: ...
IOW, the stream name & position is not displayed because the
because the current token is eof_token_entry which has no position.
This can be confusing and for sure doesn't point where the error is.
Fix this by giving to eof_token_entry the end-of-stream position.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
On code like 'goto <some reserved code>', bind_symbol() reports
an error and doesn't bind the label's ident to the goto's label
symbol.
However, at evaluation time, the ident is unconditionally
dereferenced.
Avoid the crash by checking for a null ident before dereferencing it.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Typewisely, sparse process __builtin_expect() as:
1) returning 'int'
2) taking any type in first & second arg
3) returning exactly its first argument, silently, even
if this conflicts with 1).
but this doesn't match with how gcc declare it:
long __builtin_expect(long, long);
Fix this by giving the proper prototype to this builtin
and removing the bogus 'returns an int'.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
While sparse can parse VLA, their size were left as
'undeterminated' (-1) and trying to use sizeof on VLAs
resulted in a warning 'error: cannot size the expression'.
Fix this by adding the needing code to evaluate the expressions
corresponding to the sizeof of VLAs.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
If a label is defined several times, an error is issued about it.
Nevertheless, the label is used as is and once the code is linearized
several BB are created for the same label and this create
inconsistencies. For example, some code will trigger assertion failures
in rewrite_parent_branch().
Avoid the consistencies by ignoring redefined labels.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Redefined labels create inconsistencies in BB processing.
Add a testcase for it.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Statements with an empty expression, like:
__context__();
or
__context__(x,);
are silently accepted. Worse, since NULL expressions are usually
ignored because it is assumed they have already been properly
diagnosticated, no warnings of any kind are given at some later
stage.
Fix this by explicitly checking after empty expressions and
emit an error message if needed.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The expected syntax for the __context__ statement is:
__context__(<expression>);
or
__context__(<context>, <expression>);
but originally it was just:
__context__ <expression>
In other words the parenthesis were not needed and are
still not needed when no context is given.
One problem with the current way to parse these statements is
that very few validation is done. For example, code like:
__context__;
is silently accepted, as is:
__context__ a, b;
which is of course not the same as:
__context__(a,b);
And code like:
__context__(,1);
results in a confusing error message:
error: an expression is expected before ')'
error: Expected ) in expression
error: got ,
So, given that:
* the kernel has always used the syntax with parenthesis,
* the two arguments form requires the parenthesis and thus
a function-like syntax
use a more direct, robust and simpl parsing which enforce
the function-like syntax for both forms.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The expected syntax for the __context__ statement is:
__context__(<inc/dec value>);
or
__context__(<context>, <inc/dec value>);
The distinction between the two formats is made by checking if
the expression is a PREOP with '(' as op and with an comma
expression as inner expression.
However, code like:
__context__;
or
__context__(;
crashes while trying to test the non-existing expression
(after PREOP or after the comma expression).
Fix this by testing if the expression is non-null before
dereferencing it.
Note: this fix has the merit to directly address the problem
but doesn't let a diagnostic to be issued for the case
__context__;
which is considered as perfectly valid.
The next patch will take care of this.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
No check are done if the inc/dec value of context statements
is effectively a compile-time integer value: '0' is silently
used if it is not.
Change that by using get_expression_value() when linearizing
context statements (which has the added advantage to also
slightly simplify the code).
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently the parsing of the attribute 'context' is rather
complex and uses a loop which allows 1, 2, 3 or more arguments.
But the the real syntax is only correct for 2 or 3 arguments.
Furthermore the parsing mixes calls to expect() with its own
error reporting. This is a problem because if the error has first
been reported by expect(), the returned token is 'bad_token'
which has no position so you can have error logs like:
test.c:1:43: error: Expected ( after context attribute
test.c:1:43: error: got )
builtin:0:0: error: expected context input/output values
But the 'builtin:0.0' should really be 'test.c:1.43' or, even better,
there shouldn't be a double error reporting.
Fix this by simplifying the parsing and only support 2 or 3 args.
Also, make the error messages slightly more explicit about the
nature of the error.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
|
|
It's certainly worth to have some tests but to not
slow down the testsuite and to not create a dependency
on python this test need to be run explicitely with:
./test-suite doc/cdoc.cdoc
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|