Age | Commit message (Collapse) | Author |
|
Crashing loads of uninitialized memory
proved to be a problem when implementing
unions using qbe. This patch introduces
a new UNDEF Ref to represent data that is
known to be uninitialized. Optimization
passes can make use of it to eliminate
some code. In the last compilation stages,
UNDEF is treated as the constant 0xdeaddead.
|
|
Some noisy assemblers complain
when asked to do it themselves.
|
|
It is handy to express when
the end of a block cannot be
reached. If a hlt terminator
is executed, it traps the
program.
We don't go the llvm way and
specify execution semantics as
undefined behavior.
|
|
Symbols are a useful abstraction
that occurs in both Con and Alias.
In this patch they get their own
struct. This new struct packages
a symbol name and a type; the type
tells us where the symbol name
must be interpreted (currently, in
gobal memory or in thread-local
storage).
The refactor fixed a bug in
addcon(), proving the value of
packaging symbol names with their
type.
|
|
|
|
The apple targets are not done yet.
|
|
|
|
Thanks to Paul Ouellette for reporting.
|
|
|
|
amd64 lacks instruction for this so it has to be implemented with
float -> signed casts. The approach is borrowed from llvm.
|
|
amd64 lacks an instruction for this so it has to be implemented with
signed -> float casts:
- Word casting is done by zero-extending the word to a long and then doing
a regular signed cast.
- Long casting is done by dividing by two with correct rounding if the
highest bit is set and casting that to float, then adding
1 to mantissa with integer addition
|
|
Necessary for floating-point negation, because
`%result = sub 0, %operand` doesn't give the correct sign for 0/-0.
|
|
parseref() has code to reuse address constants, but this is not
done in other passes such as fold or isel. Introduce a new function
newcon() which takes a Con and returns a Ref for that constant, and
use this whenever creating address constants.
This is necessary to fix folding of address constants when one
operand is already folded. For example, in
%a =l add $x, 1
%b =l add %a, 2
%c =w loadw %b
%a and %b were folded to $x+1 and $x+3 respectively, but then the
second add is visited again since it uses %a. This gets folded to
$x+3 as well, but as a new distinct constant. This results in %b
getting labeled as bottom instead of either constant, disabling the
replacement of %b by a constant in subsequent instructions (such
as the loadw).
|
|
|
|
This may happen in a branch QBE doesn't realize is unreachable,
for example (simplified from real code found in ncurses)
data $str = { b "abcdef", b 0 }
function l $f(w %x) {
@start
%.1 =w ceqw %x, 0
jnz %.1, @logic_join, @logic_right
@logic_right
%p =l call $strchr(l $str, w %x)
%.2 =w ceql %p, 0
@logic_join
%.3 =w phi @start %.1, @logic_right %.2
jnz %.3, @fail, @return
@fail
ret 0
@return
%.4 =l sub %p, $str
ret %.4
}
|
|
|
|
Otherwise, if a constant is stored as a float and retrieved as an
int, the padding bits are uninitialized. This can result in the
generation of invalid assembly:
Error: suffix or operands invalid for `cvtsi2ss'
Reported by Hiltjo Posthuma.
|
|
This fixes similar bugs than the ones fixed
in the previous commit.
In the folding code the invariant is that
when a result is 32 bits wide, the low 32
bits of 'x' are correct. The high bits
can be anything.
|
|
|
|
Remarks from Quentin:
It is an important decision to use Bot
and not Top as the result of 'x / 0'.
By using Bot, we refuse to give a warrant
to the compiler that would allow meaningless
subsequent decisions. An example follows.
Clang, on my computer, will build a program
which prints "Ho" when fed the following C:
int main()
{ puts(1/0 ? "Hi" : "Ho"); }
On the other hand, a C compiler based on
QBE will build a program which crashes,
as one would expect.
See also
https://c9x.me/notes/2014-09-10.html
|
|
Compiler warned about comparison between signed and unsigned values.
|
|
Symbols in the source file are still limited in
length because the rest of the code assumes that
strings always fit in NString bytes.
Regardless, there is already a benefit because
comparing/copying symbol names does not require
using strcmp()/strcpy() anymore.
|
|
This big diff does multiple changes to allow
the addition of new targets to qbe. The
changes are listed below in decreasing order
of impact.
1. Add a new Target structure.
To add support for a given target, one has to
implement all the members of the Target
structure. All the source files where changed
to use this interface where needed.
2. Single out amd64-specific code.
In this commit, the amd64 target T_amd64_sysv
is the only target available, it is implemented
in the amd64/ directory. All the non-static
items in this directory are prefixed with either
amd64_ or amd64_sysv (for items that are
specific to the System V ABI).
3. Centralize Ops information.
There is now a file 'ops.h' that must be used to
store all the available operations together with
their metadata. The various targets will only
select what they need; but it is beneficial that
there is only *one* place to change to add a new
instruction.
One good side effect of this change is that any
operation 'xyz' in the IL now as a corresponding
'Oxyz' in the code.
4. Misc fixes.
One notable change is that instruction selection
now generates generic comparison operations and
the lowering to the target's comparisons is done
in the emitter.
GAS directives for data are the same for many
targets, so data emission was extracted in a
file 'gas.c'.
5. Modularize the Makefile.
The Makefile now has a list of C files that
are target-independent (SRC), and one list
of C files per target. Each target can also
use its own 'all.h' header (for example to
define registers).
|
|
While I was at it I also refreshed some bits
in the instruction selection.
|
|
When an edge is deleted, the phis and predecessors of the
destination block have to be updated. This is what blkdel()
was doing, but at a level too coarse. The new function
edgedel() allows to remove a single edge (and takes care of
multiple edges).
|
|
|
|
|
|
The casting to uint32_t made the code for comparing
two signed words invalid. Interestingly, this can
be fixed by casting to int32_t instead. Because
sign extension is monotonic, all the unsigned
comparisons remain valid.
CVC4 can even check that for us:
x, y : BITVECTOR(32);
QUERY BVLT(SX(x, 64), SX(y, 64)) <=> BVLT(x, y);
QUERY BVLE(SX(x, 64), SX(y, 64)) <=> BVLE(x, y);
QUERY BVGT(SX(x, 64), SX(y, 64)) <=> BVGT(x, y);
QUERY BVGE(SX(x, 64), SX(y, 64)) <=> BVGE(x, y);
|
|
|
|
|
|
|
|
|
|
AFL found that.
|
|
The code computing if "the" edge of a phi
argument is live or dead was wrong. AFL
found that.
|
|
|
|
|
|
|
|
|
|
|
|
I think I should check for Top when rewriting.
Because for sure, we don't want to replace some
temporary by Top, first, I can't represent it,
and second, it'd mean that it is a temporary
used undefined.
The example that triggered the assertion was like
that:
@b0
jnz 1, @b1, @b3
@b1
%x =w phi @b0 10, @b2 %x1
jnz %x, @b2, @b3
@b2
%x1 =w sub %x, 1
jmp @b1
@b3
%x2 =w phi @b1 %x, @b0 42
So SCCP optimistically assumes %x is 10 when it goes
through @b1, then at the branch, @b1->@b3 is left for
dead. After that, @b2 is processed and the flow
worklist is empty. Now uses are processed, %x2 will
get processed regardless if its block is dead or not
(it is at that time), then all the back-edges are dead
so the phi is set to the lattice merge of tops only.
BOOM!
This leads to the question, should phis of dead blocks
be processed? I'll check that later.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|