Merge remote-tracking branch 'origin/master' into no_std
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "cretonne-tools"
|
||||
authors = ["The Cretonne Project Developers"]
|
||||
version = "0.3.4"
|
||||
version = "0.4.1"
|
||||
description = "Binaries for testing the Cretonne library"
|
||||
license = "Apache-2.0"
|
||||
documentation = "https://cretonne.readthedocs.io/"
|
||||
@@ -13,18 +13,18 @@ name = "cton-util"
|
||||
path = "src/cton-util.rs"
|
||||
|
||||
[dependencies]
|
||||
cretonne = { path = "lib/cretonne", version = "0.3.4" }
|
||||
cretonne-reader = { path = "lib/reader", version = "0.3.4" }
|
||||
cretonne-frontend = { path = "lib/frontend", version = "0.3.4" }
|
||||
cretonne-wasm = { path = "lib/wasm", version = "0.3.4" }
|
||||
cretonne-native = { path = "lib/native", version = "0.3.4" }
|
||||
filecheck = { path = "lib/filecheck" }
|
||||
cretonne = { path = "lib/cretonne", version = "0.4.1" }
|
||||
cretonne-reader = { path = "lib/reader", version = "0.4.1" }
|
||||
cretonne-frontend = { path = "lib/frontend", version = "0.4.1" }
|
||||
cretonne-wasm = { path = "lib/wasm", version = "0.4.1" }
|
||||
cretonne-native = { path = "lib/native", version = "0.4.1" }
|
||||
cretonne-filetests = { path = "lib/filetests", version = "0.4.1" }
|
||||
filecheck = "0.2.1"
|
||||
docopt = "0.8.0"
|
||||
serde = "1.0.8"
|
||||
serde_derive = "1.0.8"
|
||||
num_cpus = "1.5.1"
|
||||
tempdir="0.3.5"
|
||||
term = "0.5"
|
||||
tempdir = "0.3.5"
|
||||
term = "0.5.1"
|
||||
|
||||
[workspace]
|
||||
|
||||
|
||||
1
cranelift/clippy.toml
Normal file
1
cranelift/clippy.toml
Normal file
@@ -0,0 +1 @@
|
||||
doc-valid-idents = [ "WebAssembly", "NaN", "SetCC" ]
|
||||
@@ -1,6 +1,6 @@
|
||||
test verifier
|
||||
|
||||
function %gcd(i32 uext, i32 uext) -> i32 uext native {
|
||||
function %gcd(i32 uext, i32 uext) -> i32 uext system_v {
|
||||
fn1 = function %divmod(i32 uext, i32 uext) -> i32 uext, i32 uext
|
||||
|
||||
ebb1(v1: i32, v2: i32):
|
||||
|
||||
@@ -16,8 +16,8 @@ highlighting some of the differences and similarities. Both projects:
|
||||
- Use an ISA-agnostic input language in order to mostly abstract away the
|
||||
differences between target instruction set architectures.
|
||||
- Depend extensively on SSA form.
|
||||
- Have both textual and in-memory forms of their primary intermediate language.
|
||||
(LLVM also has a binary bitcode format; Cretonne doesn't.)
|
||||
- Have both textual and in-memory forms of their primary intermediate
|
||||
representation. (LLVM also has a binary bitcode format; Cretonne doesn't.)
|
||||
- Can target multiple ISAs.
|
||||
- Can cross-compile by default without rebuilding the code generator.
|
||||
|
||||
@@ -41,8 +41,8 @@ LLVM uses multiple intermediate representations as it translates a program to
|
||||
binary machine code:
|
||||
|
||||
`LLVM IR <https://llvm.org/docs/LangRef.html>`_
|
||||
This is the primary intermediate language which has textual, binary, and
|
||||
in-memory representations. It serves two main purposes:
|
||||
This is the primary intermediate representation which has textual, binary, and
|
||||
in-memory forms. It serves two main purposes:
|
||||
|
||||
- An ISA-agnostic, stable(ish) input language that front ends can generate
|
||||
easily.
|
||||
@@ -89,9 +89,9 @@ representation. Some target ISAs have a fast instruction selector that can
|
||||
translate simple code directly to MachineInstrs, bypassing SelectionDAG when
|
||||
possible.
|
||||
|
||||
:doc:`Cretonne <langref>` uses a single intermediate language to cover these
|
||||
levels of abstraction. This is possible in part because of Cretonne's smaller
|
||||
scope.
|
||||
:doc:`Cretonne <langref>` uses a single intermediate representation to cover
|
||||
these levels of abstraction. This is possible in part because of Cretonne's
|
||||
smaller scope.
|
||||
|
||||
- Cretonne does not provide assemblers and disassemblers, so it is not
|
||||
necessary to be able to represent every weird instruction in an ISA. Only
|
||||
@@ -102,7 +102,7 @@ scope.
|
||||
- SSA form is preserved throughout. After register allocation, each SSA value
|
||||
is annotated with an assigned ISA register or stack slot.
|
||||
|
||||
The Cretonne intermediate language is similar to LLVM IR, but at a slightly
|
||||
The Cretonne intermediate representation is similar to LLVM IR, but at a slightly
|
||||
lower level of abstraction.
|
||||
|
||||
Program structure
|
||||
@@ -112,12 +112,12 @@ In LLVM IR, the largest representable unit is the *module* which corresponds
|
||||
more or less to a C translation unit. It is a collection of functions and
|
||||
global variables that may contain references to external symbols too.
|
||||
|
||||
In Cretonne IL, the largest representable unit is the *function*. This is so
|
||||
In Cretonne IR, the largest representable unit is the *function*. This is so
|
||||
that functions can easily be compiled in parallel without worrying about
|
||||
references to shared data structures. Cretonne does not have any
|
||||
inter-procedural optimizations like inlining.
|
||||
|
||||
An LLVM IR function is a graph of *basic blocks*. A Cretonne IL function is a
|
||||
An LLVM IR function is a graph of *basic blocks*. A Cretonne IR function is a
|
||||
graph of *extended basic blocks* that may contain internal branch instructions.
|
||||
The main difference is that an LLVM conditional branch instruction has two
|
||||
target basic blocks---a true and a false edge. A Cretonne branch instruction
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Sphinx domain for documenting compiler intermediate languages.
|
||||
# Sphinx domain for documenting compiler intermediate representations.
|
||||
#
|
||||
# This defines a 'cton' Sphinx domain with the following directives and roles:
|
||||
#
|
||||
@@ -29,10 +29,10 @@ import sphinx.ext.autodoc
|
||||
|
||||
class CtonObject(ObjectDescription):
|
||||
"""
|
||||
Any kind of Cretonne IL object.
|
||||
Any kind of Cretonne IR object.
|
||||
|
||||
This is a shared base class for the different kinds of indexable objects
|
||||
in the Cretonne IL reference.
|
||||
in the Cretonne IR reference.
|
||||
"""
|
||||
option_spec = {
|
||||
'noindex': directives.flag,
|
||||
@@ -98,7 +98,7 @@ def parse_type(name, signode):
|
||||
|
||||
|
||||
class CtonType(CtonObject):
|
||||
"""A Cretonne IL type description."""
|
||||
"""A Cretonne IR type description."""
|
||||
|
||||
def handle_signature(self, sig, signode):
|
||||
"""
|
||||
@@ -112,7 +112,7 @@ class CtonType(CtonObject):
|
||||
return name
|
||||
|
||||
def get_index_text(self, name):
|
||||
return name + ' (IL type)'
|
||||
return name + ' (IR type)'
|
||||
|
||||
|
||||
sep_equal = re.compile('\s*=\s*')
|
||||
@@ -127,7 +127,7 @@ def parse_params(s, signode):
|
||||
|
||||
|
||||
class CtonInst(CtonObject):
|
||||
"""A Cretonne IL instruction."""
|
||||
"""A Cretonne IR instruction."""
|
||||
|
||||
doc_field_types = [
|
||||
TypedField('argument', label=l_('Arguments'),
|
||||
@@ -176,11 +176,11 @@ class CtonInst(CtonObject):
|
||||
|
||||
|
||||
class CtonInstGroup(CtonObject):
|
||||
"""A Cretonne IL instruction group."""
|
||||
"""A Cretonne IR instruction group."""
|
||||
|
||||
|
||||
class CretonneDomain(Domain):
|
||||
"""Cretonne domain for intermediate language objects."""
|
||||
"""Cretonne domain for IR objects."""
|
||||
name = 'cton'
|
||||
label = 'Cretonne'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
test verifier
|
||||
|
||||
function %average(i32, i32) -> f32 native {
|
||||
function %average(i32, i32) -> f32 system_v {
|
||||
ss1 = explicit_slot 8 ; Stack slot for ``sum``.
|
||||
|
||||
ebb1(v1: i32, v2: i32):
|
||||
|
||||
@@ -5,19 +5,19 @@ Cretonne Language Reference
|
||||
.. default-domain:: cton
|
||||
.. highlight:: cton
|
||||
|
||||
The Cretonne intermediate language (:term:`IL`) has two equivalent
|
||||
representations: an *in-memory data structure* that the code generator library
|
||||
is using, and a *text format* which is used for test cases and debug output.
|
||||
Files containing Cretonne textual IL have the ``.cton`` filename extension.
|
||||
The Cretonne intermediate representation (:term:`IR`) has two primary forms:
|
||||
an *in-memory data structure* that the code generator library is using, and a
|
||||
*text format* which is used for test cases and debug output.
|
||||
Files containing Cretonne textual IR have the ``.cton`` filename extension.
|
||||
|
||||
This reference uses the text format to describe IL semantics but glosses over
|
||||
This reference uses the text format to describe IR semantics but glosses over
|
||||
the finer details of the lexical and syntactic structure of the format.
|
||||
|
||||
|
||||
Overall structure
|
||||
=================
|
||||
|
||||
Cretonne compiles functions independently. A ``.cton`` IL file may contain
|
||||
Cretonne compiles functions independently. A ``.cton`` IR file may contain
|
||||
multiple functions, and the programmatic API can create multiple function
|
||||
handles at the same time, but the functions don't share any data or reference
|
||||
each other directly.
|
||||
@@ -27,7 +27,7 @@ This is a simple C function that computes the average of an array of floats:
|
||||
.. literalinclude:: example.c
|
||||
:language: c
|
||||
|
||||
Here is the same function compiled into Cretonne IL:
|
||||
Here is the same function compiled into Cretonne IR:
|
||||
|
||||
.. literalinclude:: example.cton
|
||||
:language: cton
|
||||
@@ -77,7 +77,7 @@ variable value for the next iteration.
|
||||
|
||||
The `cton_frontend` crate contains utilities for translating from programs
|
||||
containing multiple assignments to the same variables into SSA form for
|
||||
Cretonne :term:`IL`.
|
||||
Cretonne :term:`IR`.
|
||||
|
||||
Such variables can also be presented to Cretonne as :term:`stack slot`\s.
|
||||
Stack slots are accessed with the :inst:`stack_store` and :inst:`stack_load`
|
||||
@@ -303,7 +303,7 @@ indicate the different kinds of immediate operands on an instruction.
|
||||
A floating point condition code. See the :inst:`fcmp` instruction for details.
|
||||
|
||||
The two IEEE floating point immediate types :type:`ieee32` and :type:`ieee64`
|
||||
are displayed as hexadecimal floating point literals in the textual :term:`IL`
|
||||
are displayed as hexadecimal floating point literals in the textual :term:`IR`
|
||||
format. Decimal floating point literals are not allowed because some computer
|
||||
systems can round differently when converting to binary. The hexadecimal
|
||||
floating point format is mostly the same as the one used by C99, but extended
|
||||
@@ -400,11 +400,11 @@ convention:
|
||||
param : type [paramext] [paramspecial]
|
||||
paramext : "uext" | "sext"
|
||||
paramspecial : "sret" | "link" | "fp" | "csr" | "vmctx"
|
||||
callconv : "native" | "spiderwasm"
|
||||
callconv : "system_v" | "spiderwasm"
|
||||
|
||||
Parameters and return values have flags whose meaning is mostly target
|
||||
dependent. They make it possible to call native functions on the target
|
||||
platform. When calling other Cretonne functions, the flags are not necessary.
|
||||
dependent. These flags support interfacing with code produced by other
|
||||
compilers.
|
||||
|
||||
Functions that are called directly must be declared in the :term:`function
|
||||
preamble`:
|
||||
@@ -563,7 +563,7 @@ runtime data structures.
|
||||
alignment for storing a pointer.
|
||||
|
||||
Chains of ``deref`` global variables are possible, but cycles are not
|
||||
allowed. They will be caught by the IL verifier.
|
||||
allowed. They will be caught by the IR verifier.
|
||||
|
||||
:arg BaseGV: Global variable containing the base pointer.
|
||||
:arg Offset: Byte offset from the loaded base pointer to the global
|
||||
@@ -654,6 +654,11 @@ trap when accessed.
|
||||
address space reserved for the heap, not including the guard pages.
|
||||
:arg GuardBytes: Size of the guard pages in bytes.
|
||||
|
||||
When the base is a global variable, it must be :term:`accessible` and naturally
|
||||
aligned for a pointer value.
|
||||
|
||||
The ``reserved_reg`` option is not yet implemented.
|
||||
|
||||
Dynamic heaps
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
@@ -672,6 +677,11 @@ is resized. The bound of a dynamic heap is stored in a global variable.
|
||||
:arg BoundGV: Global variable containing the current heap bound in bytes.
|
||||
:arg GuardBytes: Size of the guard pages in bytes.
|
||||
|
||||
When the base is a global variable, it must be :term:`accessible` and naturally
|
||||
aligned for a pointer value.
|
||||
|
||||
The ``reserved_reg`` option is not yet implemented.
|
||||
|
||||
Heap examples
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
@@ -1144,19 +1154,11 @@ Glossary
|
||||
The extended basic blocks which contain all the executable code in a
|
||||
function. The function body follows the function preamble.
|
||||
|
||||
intermediate language
|
||||
IL
|
||||
The language used to describe functions to Cretonne. This reference
|
||||
describes the syntax and semantics of the Cretonne IL. The IL has two
|
||||
forms: Textual and an in-memory intermediate representation
|
||||
(:term:`IR`).
|
||||
|
||||
intermediate representation
|
||||
IR
|
||||
The in-memory representation of :term:`IL`. The data structures
|
||||
Cretonne uses to represent a program internally are called the
|
||||
intermediate representation. Cretonne's IR can be converted to text
|
||||
losslessly.
|
||||
The language used to describe functions to Cretonne. This reference
|
||||
describes the syntax and semantics of Cretonne IR. The IR has two
|
||||
forms: Textual, and an in-memory data structure.
|
||||
|
||||
stack slot
|
||||
A fixed size memory allocation in the current function's activation
|
||||
|
||||
@@ -89,7 +89,7 @@ easier to provide substantial input functions for the compiler tests.
|
||||
|
||||
File tests are :file:`*.cton` files in the :file:`filetests/` directory
|
||||
hierarchy. Each file has a header describing what to test followed by a number
|
||||
of input functions in the :doc:`Cretonne textual intermediate language
|
||||
of input functions in the :doc:`Cretonne textual intermediate representation
|
||||
<langref>`:
|
||||
|
||||
.. productionlist::
|
||||
@@ -136,13 +136,15 @@ This example will run the legalizer test twice. Both runs will have
|
||||
``opt_level=best``, but they will have different ``is_64bit`` settings. The 32-bit
|
||||
run will also have the RISC-V specific flag ``supports_m`` disabled.
|
||||
|
||||
The filetests are run automatically as part of `cargo test`, and they can
|
||||
also be run manually with the `cton-util test` command.
|
||||
|
||||
Filecheck
|
||||
---------
|
||||
|
||||
Many of the test commands described below use *filecheck* to verify their
|
||||
output. Filecheck is a Rust implementation of the LLVM tool of the same name.
|
||||
See the :file:`lib/filecheck` `documentation <https://docs.rs/filecheck/>`_ for
|
||||
details of its syntax.
|
||||
See the `documentation <https://docs.rs/filecheck/>`_ for details of its syntax.
|
||||
|
||||
Comments in :file:`.cton` files are associated with the entity they follow.
|
||||
This typically means an instruction or the whole function. Those tests that
|
||||
@@ -164,7 +166,7 @@ Cretonne's tests don't need this.
|
||||
----------
|
||||
|
||||
This is one of the simplest file tests, used for testing the conversion to and
|
||||
from textual IL. The ``test cat`` command simply parses each function and
|
||||
from textual IR. The ``test cat`` command simply parses each function and
|
||||
converts it back to text again. The text of each function is then matched
|
||||
against the associated filecheck directives.
|
||||
|
||||
@@ -186,7 +188,7 @@ Example::
|
||||
`test verifier`
|
||||
---------------
|
||||
|
||||
Run each function through the IL verifier and check that it produces the
|
||||
Run each function through the IR verifier and check that it produces the
|
||||
expected error messages.
|
||||
|
||||
Expected error messages are indicated with an ``error:`` directive *on the
|
||||
@@ -324,6 +326,38 @@ Test the simple GVN pass.
|
||||
The simple GVN pass is run on each function, and then results are run
|
||||
through filecheck.
|
||||
|
||||
`test licm`
|
||||
-----------------
|
||||
|
||||
Test the LICM pass.
|
||||
|
||||
The LICM pass is run on each function, and then results are run
|
||||
through filecheck.
|
||||
|
||||
`test dce`
|
||||
-----------------
|
||||
|
||||
Test the DCE pass.
|
||||
|
||||
The DCE pass is run on each function, and then results are run
|
||||
through filecheck.
|
||||
|
||||
`test preopt`
|
||||
-----------------
|
||||
|
||||
Test the preopt pass.
|
||||
|
||||
The preopt pass is run on each function, and then results are run
|
||||
through filecheck.
|
||||
|
||||
`test postopt`
|
||||
-----------------
|
||||
|
||||
Test the postopt pass.
|
||||
|
||||
The postopt pass is run on each function, and then results are run
|
||||
through filecheck.
|
||||
|
||||
`test compile`
|
||||
--------------
|
||||
|
||||
@@ -333,4 +367,4 @@ Each function is passed through the full ``Context::compile()`` function
|
||||
which is normally used to compile code. This type of test often depends
|
||||
on assertions or verifier errors, but it is also possible to use
|
||||
filecheck directives which will be matched against the final form of the
|
||||
Cretonne IL right before binary machine code emission.
|
||||
Cretonne IR right before binary machine code emission.
|
||||
|
||||
46
cranelift/filetests/dce/basic.cton
Normal file
46
cranelift/filetests/dce/basic.cton
Normal file
@@ -0,0 +1,46 @@
|
||||
test dce
|
||||
|
||||
function %simple() -> i32 {
|
||||
ebb0:
|
||||
v2 = iconst.i32 2
|
||||
v3 = iconst.i32 3
|
||||
return v3
|
||||
}
|
||||
; sameln: function %simple
|
||||
; nextln: ebb0:
|
||||
; nextln: v3 = iconst.i32 3
|
||||
; nextln: return v3
|
||||
; nextln: }
|
||||
|
||||
function %some_branching(i32, i32) -> i32 {
|
||||
ebb0(v0: i32, v1: i32):
|
||||
v3 = iconst.i32 70
|
||||
v4 = iconst.i32 71
|
||||
v5 = iconst.i32 72
|
||||
v8 = iconst.i32 73
|
||||
brz v0, ebb1
|
||||
jump ebb2(v8)
|
||||
|
||||
ebb1:
|
||||
v2 = iadd v0, v3
|
||||
return v0
|
||||
|
||||
ebb2(v9: i32):
|
||||
v6 = iadd v1, v4
|
||||
v7 = iadd v6, v9
|
||||
return v7
|
||||
}
|
||||
; sameln: function %some_branching
|
||||
; nextln: ebb0(v0: i32, v1: i32):
|
||||
; nextln: v4 = iconst.i32 71
|
||||
; nextln: v8 = iconst.i32 73
|
||||
; nextln: brz v0, ebb1
|
||||
; nextln: jump ebb2(v8)
|
||||
; nextln:
|
||||
; nextln: ebb1:
|
||||
; nextln: return v0
|
||||
; nextln:
|
||||
; nextln: ebb2(v9: i32):
|
||||
; nextln: v6 = iadd.i32 v1, v4
|
||||
; nextln: v7 = iadd v6, v9
|
||||
; nextln: return v7
|
||||
@@ -59,7 +59,7 @@ function %test(i32) {
|
||||
; nextln: ebb5:
|
||||
; nextln: }
|
||||
|
||||
function %loop2(i32) native {
|
||||
function %loop2(i32) system_v {
|
||||
ebb0(v0: i32):
|
||||
brz v0, ebb1 ; dominates: ebb1 ebb3 ebb4 ebb5
|
||||
jump ebb2 ; dominates: ebb2
|
||||
|
||||
@@ -43,7 +43,7 @@ function %loop1(i32) {
|
||||
; nextln: ebb9:
|
||||
; nextln: }
|
||||
|
||||
function %loop2(i32) native {
|
||||
function %loop2(i32) system_v {
|
||||
ebb0(v0: i32):
|
||||
brz v0, ebb1 ; dominates: ebb1 ebb3 ebb4 ebb5
|
||||
jump ebb2 ; dominates: ebb2
|
||||
|
||||
@@ -2,7 +2,7 @@ test compile
|
||||
set is_64bit=1
|
||||
isa intel haswell
|
||||
|
||||
function %foo(i64, i64, i64, i32) -> b1 native {
|
||||
function %foo(i64, i64, i64, i32) -> b1 system_v {
|
||||
ebb3(v0: i64, v1: i64, v2: i64, v3: i32):
|
||||
v5 = icmp ne v2, v2
|
||||
v8 = iconst.i64 0
|
||||
|
||||
@@ -5,14 +5,14 @@ isa intel
|
||||
; regex: V=v\d+
|
||||
|
||||
function %f() {
|
||||
sig0 = (i32) -> i32 native
|
||||
; check: sig0 = (i32 [0]) -> i32 [%rax] native
|
||||
sig0 = (i32) -> i32 system_v
|
||||
; check: sig0 = (i32 [0]) -> i32 [%rax] system_v
|
||||
|
||||
sig1 = (i64) -> b1 native
|
||||
; check: sig1 = (i32 [0], i32 [4]) -> b1 [%rax] native
|
||||
sig1 = (i64) -> b1 system_v
|
||||
; check: sig1 = (i32 [0], i32 [4]) -> b1 [%rax] system_v
|
||||
|
||||
sig2 = (f32, i64) -> f64 native
|
||||
; check: sig2 = (f32 [0], i32 [4], i32 [8]) -> f64 [%xmm0] native
|
||||
sig2 = (f32, i64) -> f64 system_v
|
||||
; check: sig2 = (f32 [0], i32 [4], i32 [8]) -> f64 [%xmm0] system_v
|
||||
|
||||
ebb0:
|
||||
return
|
||||
|
||||
@@ -6,14 +6,14 @@ isa intel
|
||||
; regex: V=v\d+
|
||||
|
||||
function %f() {
|
||||
sig0 = (i32) -> i32 native
|
||||
; check: sig0 = (i32 [%rdi]) -> i32 [%rax] native
|
||||
sig0 = (i32) -> i32 system_v
|
||||
; check: sig0 = (i32 [%rdi]) -> i32 [%rax] system_v
|
||||
|
||||
sig1 = (i64) -> b1 native
|
||||
; check: sig1 = (i64 [%rdi]) -> b1 [%rax] native
|
||||
sig1 = (i64) -> b1 system_v
|
||||
; check: sig1 = (i64 [%rdi]) -> b1 [%rax] system_v
|
||||
|
||||
sig2 = (f32, i64) -> f64 native
|
||||
; check: sig2 = (f32 [%xmm0], i64 [%rdi]) -> f64 [%xmm0] native
|
||||
sig2 = (f32, i64) -> f64 system_v
|
||||
; check: sig2 = (f32 [%xmm0], i64 [%rdi]) -> f64 [%xmm0] system_v
|
||||
|
||||
ebb0:
|
||||
return
|
||||
|
||||
@@ -47,28 +47,23 @@ ebb1(v20: i32):
|
||||
function %i64_popcount(i64) -> i64 {
|
||||
ebb0(v30: i64):
|
||||
v31 = popcnt v30;
|
||||
; check: iconst.i32
|
||||
; check: ushr
|
||||
; check: ushr_imm
|
||||
; check: iconst.i64
|
||||
; check: band
|
||||
; check: isub
|
||||
; check: iconst.i32
|
||||
; check: ushr
|
||||
; check: ushr_imm
|
||||
; check: band
|
||||
; check: isub
|
||||
; check: iconst.i32
|
||||
; check: ushr
|
||||
; check: ushr_imm
|
||||
; check: band
|
||||
; check: isub
|
||||
; check: iconst.i32
|
||||
; check: ushr
|
||||
; check: ushr_imm
|
||||
; check: iadd
|
||||
; check: iconst.i64
|
||||
; check: band
|
||||
; check: iconst.i64
|
||||
; check: imul
|
||||
; check: iconst.i32
|
||||
; check: ushr
|
||||
; check: ushr_imm
|
||||
return v31;
|
||||
}
|
||||
|
||||
@@ -78,27 +73,22 @@ ebb0(v30: i64):
|
||||
function %i32_popcount(i32) -> i32 {
|
||||
ebb0(v40: i32):
|
||||
v41 = popcnt v40;
|
||||
; check: iconst.i32
|
||||
; check: ushr
|
||||
; check: ushr_imm
|
||||
; check: iconst.i32
|
||||
; check: band
|
||||
; check: isub
|
||||
; check: iconst.i32
|
||||
; check: ushr
|
||||
; check: ushr_imm
|
||||
; check: band
|
||||
; check: isub
|
||||
; check: iconst.i32
|
||||
; check: ushr
|
||||
; check: ushr_imm
|
||||
; check: band
|
||||
; check: isub
|
||||
; check: iconst.i32
|
||||
; check: ushr
|
||||
; check: ushr_imm
|
||||
; check: iadd
|
||||
; check: iconst.i32
|
||||
; check: band
|
||||
; check: iconst.i32
|
||||
; check: imul
|
||||
; check: iconst.i32
|
||||
; check: ushr
|
||||
; check: ushr_imm
|
||||
return v41;
|
||||
}
|
||||
|
||||
@@ -15,56 +15,56 @@ ebb0:
|
||||
|
||||
[-,%r11] v10 = iconst.i64 0x1234
|
||||
; asm: bsfq %r11, %rcx
|
||||
[-,%rcx,%eflags] v11, v12 = x86_bsf v10 ; bin: 49 0f bc cb
|
||||
[-,%rcx,%rflags] v11, v12 = x86_bsf v10 ; bin: 49 0f bc cb
|
||||
|
||||
[-,%rdx] v14 = iconst.i64 0x5678
|
||||
; asm: bsfq %rdx, %r12
|
||||
[-,%r12,%eflags] v15, v16 = x86_bsf v14 ; bin: 4c 0f bc e2
|
||||
[-,%r12,%rflags] v15, v16 = x86_bsf v14 ; bin: 4c 0f bc e2
|
||||
|
||||
; asm: bsfq %rdx, %rdi
|
||||
[-,%rdi,%eflags] v17, v18 = x86_bsf v14 ; bin: 48 0f bc fa
|
||||
[-,%rdi,%rflags] v17, v18 = x86_bsf v14 ; bin: 48 0f bc fa
|
||||
|
||||
|
||||
; 32-bit wide bsf
|
||||
|
||||
[-,%r11] v20 = iconst.i32 0x1234
|
||||
; asm: bsfl %r11d, %ecx
|
||||
[-,%rcx,%eflags] v21, v22 = x86_bsf v20 ; bin: 41 0f bc cb
|
||||
[-,%rcx,%rflags] v21, v22 = x86_bsf v20 ; bin: 41 0f bc cb
|
||||
|
||||
[-,%rdx] v24 = iconst.i32 0x5678
|
||||
; asm: bsfl %edx, %r12d
|
||||
[-,%r12,%eflags] v25, v26 = x86_bsf v24 ; bin: 44 0f bc e2
|
||||
[-,%r12,%rflags] v25, v26 = x86_bsf v24 ; bin: 44 0f bc e2
|
||||
|
||||
; asm: bsfl %edx, %esi
|
||||
[-,%rsi,%eflags] v27, v28 = x86_bsf v24 ; bin: 0f bc f2
|
||||
[-,%rsi,%rflags] v27, v28 = x86_bsf v24 ; bin: 0f bc f2
|
||||
|
||||
|
||||
; 64-bit wide bsr
|
||||
|
||||
[-,%r11] v30 = iconst.i64 0x1234
|
||||
; asm: bsrq %r11, %rcx
|
||||
[-,%rcx,%eflags] v31, v32 = x86_bsr v30 ; bin: 49 0f bd cb
|
||||
[-,%rcx,%rflags] v31, v32 = x86_bsr v30 ; bin: 49 0f bd cb
|
||||
|
||||
[-,%rdx] v34 = iconst.i64 0x5678
|
||||
; asm: bsrq %rdx, %r12
|
||||
[-,%r12,%eflags] v35, v36 = x86_bsr v34 ; bin: 4c 0f bd e2
|
||||
[-,%r12,%rflags] v35, v36 = x86_bsr v34 ; bin: 4c 0f bd e2
|
||||
|
||||
; asm: bsrq %rdx, %rdi
|
||||
[-,%rdi,%eflags] v37, v38 = x86_bsr v34 ; bin: 48 0f bd fa
|
||||
[-,%rdi,%rflags] v37, v38 = x86_bsr v34 ; bin: 48 0f bd fa
|
||||
|
||||
|
||||
; 32-bit wide bsr
|
||||
|
||||
[-,%r11] v40 = iconst.i32 0x1234
|
||||
; asm: bsrl %r11d, %ecx
|
||||
[-,%rcx,%eflags] v41, v42 = x86_bsr v40 ; bin: 41 0f bd cb
|
||||
[-,%rcx,%rflags] v41, v42 = x86_bsr v40 ; bin: 41 0f bd cb
|
||||
|
||||
[-,%rdx] v44 = iconst.i32 0x5678
|
||||
; asm: bsrl %edx, %r12d
|
||||
[-,%r12,%eflags] v45, v46 = x86_bsr v44 ; bin: 44 0f bd e2
|
||||
[-,%r12,%rflags] v45, v46 = x86_bsr v44 ; bin: 44 0f bd e2
|
||||
|
||||
; asm: bsrl %edx, %esi
|
||||
[-,%rsi,%eflags] v47, v48 = x86_bsr v44 ; bin: 0f bd f2
|
||||
[-,%rsi,%rflags] v47, v48 = x86_bsr v44 ; bin: 0f bd f2
|
||||
|
||||
|
||||
; 64-bit wide cmov
|
||||
|
||||
@@ -147,48 +147,48 @@ ebb0:
|
||||
|
||||
; Load/Store
|
||||
|
||||
; asm: movd (%ecx), %xmm5
|
||||
[-,%xmm5] v100 = load.f32 v0 ; bin: 66 0f 6e 29
|
||||
; asm: movd (%esi), %xmm2
|
||||
[-,%xmm2] v101 = load.f32 v1 ; bin: 66 0f 6e 16
|
||||
; asm: movd 50(%ecx), %xmm5
|
||||
[-,%xmm5] v110 = load.f32 v0+50 ; bin: 66 0f 6e 69 32
|
||||
; asm: movd -50(%esi), %xmm2
|
||||
[-,%xmm2] v111 = load.f32 v1-50 ; bin: 66 0f 6e 56 ce
|
||||
; asm: movd 10000(%ecx), %xmm5
|
||||
[-,%xmm5] v120 = load.f32 v0+10000 ; bin: 66 0f 6e a9 00002710
|
||||
; asm: movd -10000(%esi), %xmm2
|
||||
[-,%xmm2] v121 = load.f32 v1-10000 ; bin: 66 0f 6e 96 ffffd8f0
|
||||
; asm: movss (%ecx), %xmm5
|
||||
[-,%xmm5] v100 = load.f32 v0 ; bin: heap_oob f3 0f 10 29
|
||||
; asm: movss (%esi), %xmm2
|
||||
[-,%xmm2] v101 = load.f32 v1 ; bin: heap_oob f3 0f 10 16
|
||||
; asm: movss 50(%ecx), %xmm5
|
||||
[-,%xmm5] v110 = load.f32 v0+50 ; bin: heap_oob f3 0f 10 69 32
|
||||
; asm: movss -50(%esi), %xmm2
|
||||
[-,%xmm2] v111 = load.f32 v1-50 ; bin: heap_oob f3 0f 10 56 ce
|
||||
; asm: movss 10000(%ecx), %xmm5
|
||||
[-,%xmm5] v120 = load.f32 v0+10000 ; bin: heap_oob f3 0f 10 a9 00002710
|
||||
; asm: movss -10000(%esi), %xmm2
|
||||
[-,%xmm2] v121 = load.f32 v1-10000 ; bin: heap_oob f3 0f 10 96 ffffd8f0
|
||||
|
||||
; asm: movd %xmm5, (%ecx)
|
||||
[-] store.f32 v100, v0 ; bin: 66 0f 7e 29
|
||||
; asm: movd %xmm2, (%esi)
|
||||
[-] store.f32 v101, v1 ; bin: 66 0f 7e 16
|
||||
; asm: movd %xmm5, 50(%ecx)
|
||||
[-] store.f32 v100, v0+50 ; bin: 66 0f 7e 69 32
|
||||
; asm: movd %xmm2, -50(%esi)
|
||||
[-] store.f32 v101, v1-50 ; bin: 66 0f 7e 56 ce
|
||||
; asm: movd %xmm5, 10000(%ecx)
|
||||
[-] store.f32 v100, v0+10000 ; bin: 66 0f 7e a9 00002710
|
||||
; asm: movd %xmm2, -10000(%esi)
|
||||
[-] store.f32 v101, v1-10000 ; bin: 66 0f 7e 96 ffffd8f0
|
||||
; asm: movss %xmm5, (%ecx)
|
||||
[-] store.f32 v100, v0 ; bin: heap_oob f3 0f 11 29
|
||||
; asm: movss %xmm2, (%esi)
|
||||
[-] store.f32 v101, v1 ; bin: heap_oob f3 0f 11 16
|
||||
; asm: movss %xmm5, 50(%ecx)
|
||||
[-] store.f32 v100, v0+50 ; bin: heap_oob f3 0f 11 69 32
|
||||
; asm: movss %xmm2, -50(%esi)
|
||||
[-] store.f32 v101, v1-50 ; bin: heap_oob f3 0f 11 56 ce
|
||||
; asm: movss %xmm5, 10000(%ecx)
|
||||
[-] store.f32 v100, v0+10000 ; bin: heap_oob f3 0f 11 a9 00002710
|
||||
; asm: movss %xmm2, -10000(%esi)
|
||||
[-] store.f32 v101, v1-10000 ; bin: heap_oob f3 0f 11 96 ffffd8f0
|
||||
|
||||
; Spill / Fill.
|
||||
|
||||
; asm: movd %xmm5, 1032(%esp)
|
||||
[-,ss1] v200 = spill v100 ; bin: 66 0f 7e ac 24 00000408
|
||||
; asm: movd %xmm2, 1032(%esp)
|
||||
[-,ss1] v201 = spill v101 ; bin: 66 0f 7e 94 24 00000408
|
||||
; asm: movss %xmm5, 1032(%esp)
|
||||
[-,ss1] v200 = spill v100 ; bin: f3 0f 11 ac 24 00000408
|
||||
; asm: movss %xmm2, 1032(%esp)
|
||||
[-,ss1] v201 = spill v101 ; bin: f3 0f 11 94 24 00000408
|
||||
|
||||
; asm: movd 1032(%esp), %xmm5
|
||||
[-,%xmm5] v210 = fill v200 ; bin: 66 0f 6e ac 24 00000408
|
||||
; asm: movd 1032(%esp), %xmm2
|
||||
[-,%xmm2] v211 = fill v201 ; bin: 66 0f 6e 94 24 00000408
|
||||
; asm: movss 1032(%esp), %xmm5
|
||||
[-,%xmm5] v210 = fill v200 ; bin: f3 0f 10 ac 24 00000408
|
||||
; asm: movss 1032(%esp), %xmm2
|
||||
[-,%xmm2] v211 = fill v201 ; bin: f3 0f 10 94 24 00000408
|
||||
|
||||
; asm: movd %xmm5, 1032(%rsp)
|
||||
regspill v100, %xmm5 -> ss1 ; bin: 66 0f 7e ac 24 00000408
|
||||
; asm: movd 1032(%rsp), %xmm5
|
||||
regfill v100, ss1 -> %xmm5 ; bin: 66 0f 6e ac 24 00000408
|
||||
; asm: movss %xmm5, 1032(%rsp)
|
||||
regspill v100, %xmm5 -> ss1 ; bin: f3 0f 11 ac 24 00000408
|
||||
; asm: movss 1032(%rsp), %xmm5
|
||||
regfill v100, ss1 -> %xmm5 ; bin: f3 0f 10 ac 24 00000408
|
||||
|
||||
; Comparisons.
|
||||
;
|
||||
@@ -221,11 +221,11 @@ ebb0:
|
||||
[-,%rdx] v307 = fcmp ule v11, v10 ; bin: 0f 2e d5 0f 96 c2
|
||||
|
||||
; asm: ucomiss %xmm2, %xmm5
|
||||
[-,%eflags] v310 = ffcmp v10, v11 ; bin: 0f 2e ea
|
||||
[-,%rflags] v310 = ffcmp v10, v11 ; bin: 0f 2e ea
|
||||
; asm: ucomiss %xmm2, %xmm5
|
||||
[-,%eflags] v311 = ffcmp v11, v10 ; bin: 0f 2e d5
|
||||
[-,%rflags] v311 = ffcmp v11, v10 ; bin: 0f 2e d5
|
||||
; asm: ucomiss %xmm5, %xmm5
|
||||
[-,%eflags] v312 = ffcmp v10, v10 ; bin: 0f 2e ed
|
||||
[-,%rflags] v312 = ffcmp v10, v10 ; bin: 0f 2e ed
|
||||
|
||||
return
|
||||
}
|
||||
@@ -362,48 +362,48 @@ ebb0:
|
||||
|
||||
; Load/Store
|
||||
|
||||
; asm: movq (%ecx), %xmm5
|
||||
[-,%xmm5] v100 = load.f64 v0 ; bin: f3 0f 7e 29
|
||||
; asm: movq (%esi), %xmm2
|
||||
[-,%xmm2] v101 = load.f64 v1 ; bin: f3 0f 7e 16
|
||||
; asm: movq 50(%ecx), %xmm5
|
||||
[-,%xmm5] v110 = load.f64 v0+50 ; bin: f3 0f 7e 69 32
|
||||
; asm: movq -50(%esi), %xmm2
|
||||
[-,%xmm2] v111 = load.f64 v1-50 ; bin: f3 0f 7e 56 ce
|
||||
; asm: movq 10000(%ecx), %xmm5
|
||||
[-,%xmm5] v120 = load.f64 v0+10000 ; bin: f3 0f 7e a9 00002710
|
||||
; asm: movq -10000(%esi), %xmm2
|
||||
[-,%xmm2] v121 = load.f64 v1-10000 ; bin: f3 0f 7e 96 ffffd8f0
|
||||
; asm: movsd (%ecx), %xmm5
|
||||
[-,%xmm5] v100 = load.f64 v0 ; bin: heap_oob f2 0f 10 29
|
||||
; asm: movsd (%esi), %xmm2
|
||||
[-,%xmm2] v101 = load.f64 v1 ; bin: heap_oob f2 0f 10 16
|
||||
; asm: movsd 50(%ecx), %xmm5
|
||||
[-,%xmm5] v110 = load.f64 v0+50 ; bin: heap_oob f2 0f 10 69 32
|
||||
; asm: movsd -50(%esi), %xmm2
|
||||
[-,%xmm2] v111 = load.f64 v1-50 ; bin: heap_oob f2 0f 10 56 ce
|
||||
; asm: movsd 10000(%ecx), %xmm5
|
||||
[-,%xmm5] v120 = load.f64 v0+10000 ; bin: heap_oob f2 0f 10 a9 00002710
|
||||
; asm: movsd -10000(%esi), %xmm2
|
||||
[-,%xmm2] v121 = load.f64 v1-10000 ; bin: heap_oob f2 0f 10 96 ffffd8f0
|
||||
|
||||
; asm: movq %xmm5, (%ecx)
|
||||
[-] store.f64 v100, v0 ; bin: 66 0f d6 29
|
||||
; asm: movq %xmm2, (%esi)
|
||||
[-] store.f64 v101, v1 ; bin: 66 0f d6 16
|
||||
; asm: movq %xmm5, 50(%ecx)
|
||||
[-] store.f64 v100, v0+50 ; bin: 66 0f d6 69 32
|
||||
; asm: movq %xmm2, -50(%esi)
|
||||
[-] store.f64 v101, v1-50 ; bin: 66 0f d6 56 ce
|
||||
; asm: movq %xmm5, 10000(%ecx)
|
||||
[-] store.f64 v100, v0+10000 ; bin: 66 0f d6 a9 00002710
|
||||
; asm: movq %xmm2, -10000(%esi)
|
||||
[-] store.f64 v101, v1-10000 ; bin: 66 0f d6 96 ffffd8f0
|
||||
; asm: movsd %xmm5, (%ecx)
|
||||
[-] store.f64 v100, v0 ; bin: heap_oob f2 0f 11 29
|
||||
; asm: movsd %xmm2, (%esi)
|
||||
[-] store.f64 v101, v1 ; bin: heap_oob f2 0f 11 16
|
||||
; asm: movsd %xmm5, 50(%ecx)
|
||||
[-] store.f64 v100, v0+50 ; bin: heap_oob f2 0f 11 69 32
|
||||
; asm: movsd %xmm2, -50(%esi)
|
||||
[-] store.f64 v101, v1-50 ; bin: heap_oob f2 0f 11 56 ce
|
||||
; asm: movsd %xmm5, 10000(%ecx)
|
||||
[-] store.f64 v100, v0+10000 ; bin: heap_oob f2 0f 11 a9 00002710
|
||||
; asm: movsd %xmm2, -10000(%esi)
|
||||
[-] store.f64 v101, v1-10000 ; bin: heap_oob f2 0f 11 96 ffffd8f0
|
||||
|
||||
; Spill / Fill.
|
||||
|
||||
; asm: movq %xmm5, 1032(%esp)
|
||||
[-,ss1] v200 = spill v100 ; bin: 66 0f d6 ac 24 00000408
|
||||
; asm: movq %xmm2, 1032(%esp)
|
||||
[-,ss1] v201 = spill v101 ; bin: 66 0f d6 94 24 00000408
|
||||
; asm: movsd %xmm5, 1032(%esp)
|
||||
[-,ss1] v200 = spill v100 ; bin: f2 0f 11 ac 24 00000408
|
||||
; asm: movsd %xmm2, 1032(%esp)
|
||||
[-,ss1] v201 = spill v101 ; bin: f2 0f 11 94 24 00000408
|
||||
|
||||
; asm: movq 1032(%esp), %xmm5
|
||||
[-,%xmm5] v210 = fill v200 ; bin: f3 0f 7e ac 24 00000408
|
||||
; asm: movq 1032(%esp), %xmm2
|
||||
[-,%xmm2] v211 = fill v201 ; bin: f3 0f 7e 94 24 00000408
|
||||
; asm: movsd 1032(%esp), %xmm5
|
||||
[-,%xmm5] v210 = fill v200 ; bin: f2 0f 10 ac 24 00000408
|
||||
; asm: movsd 1032(%esp), %xmm2
|
||||
[-,%xmm2] v211 = fill v201 ; bin: f2 0f 10 94 24 00000408
|
||||
|
||||
; asm: movq %xmm5, 1032(%rsp)
|
||||
regspill v100, %xmm5 -> ss1 ; bin: 66 0f d6 ac 24 00000408
|
||||
; asm: movq 1032(%rsp), %xmm5
|
||||
regfill v100, ss1 -> %xmm5 ; bin: f3 0f 7e ac 24 00000408
|
||||
; asm: movsd %xmm5, 1032(%rsp)
|
||||
regspill v100, %xmm5 -> ss1 ; bin: f2 0f 11 ac 24 00000408
|
||||
; asm: movsd 1032(%rsp), %xmm5
|
||||
regfill v100, ss1 -> %xmm5 ; bin: f2 0f 10 ac 24 00000408
|
||||
|
||||
; Comparisons.
|
||||
;
|
||||
@@ -436,11 +436,11 @@ ebb0:
|
||||
[-,%rdx] v307 = fcmp ule v11, v10 ; bin: 66 0f 2e d5 0f 96 c2
|
||||
|
||||
; asm: ucomisd %xmm2, %xmm5
|
||||
[-,%eflags] v310 = ffcmp v10, v11 ; bin: 66 0f 2e ea
|
||||
[-,%rflags] v310 = ffcmp v10, v11 ; bin: 66 0f 2e ea
|
||||
; asm: ucomisd %xmm2, %xmm5
|
||||
[-,%eflags] v311 = ffcmp v11, v10 ; bin: 66 0f 2e d5
|
||||
[-,%rflags] v311 = ffcmp v11, v10 ; bin: 66 0f 2e d5
|
||||
; asm: ucomisd %xmm5, %xmm5
|
||||
[-,%eflags] v312 = ffcmp v10, v10 ; bin: 66 0f 2e ed
|
||||
[-,%rflags] v312 = ffcmp v10, v10 ; bin: 66 0f 2e ed
|
||||
|
||||
return
|
||||
}
|
||||
@@ -448,7 +448,7 @@ ebb0:
|
||||
function %cpuflags_float(f32 [%xmm0]) {
|
||||
ebb0(v0: f32 [%xmm0]):
|
||||
; asm: ucomiss %xmm0, %xmm0
|
||||
[-,%eflags] v1 = ffcmp v0, v0 ; bin: 0f 2e c0
|
||||
[-,%rflags] v1 = ffcmp v0, v0 ; bin: 0f 2e c0
|
||||
|
||||
jump ebb1
|
||||
|
||||
@@ -471,21 +471,21 @@ ebb1:
|
||||
brff ule v1, ebb1 ; bin: 76 f0
|
||||
|
||||
; asm: jp .+4; ud2
|
||||
trapff ord v1, user0 ; bin: 7a 02 0f 0b
|
||||
trapff ord v1, user0 ; bin: 7a 02 user0 0f 0b
|
||||
; asm: jnp .+4; ud2
|
||||
trapff uno v1, user0 ; bin: 7b 02 0f 0b
|
||||
trapff uno v1, user0 ; bin: 7b 02 user0 0f 0b
|
||||
; asm: je .+4; ud2
|
||||
trapff one v1, user0 ; bin: 74 02 0f 0b
|
||||
trapff one v1, user0 ; bin: 74 02 user0 0f 0b
|
||||
; asm: jne .+4; ud2
|
||||
trapff ueq v1, user0 ; bin: 75 02 0f 0b
|
||||
trapff ueq v1, user0 ; bin: 75 02 user0 0f 0b
|
||||
; asm: jna .+4; ud2
|
||||
trapff gt v1, user0 ; bin: 76 02 0f 0b
|
||||
trapff gt v1, user0 ; bin: 76 02 user0 0f 0b
|
||||
; asm: jnae .+4; ud2
|
||||
trapff ge v1, user0 ; bin: 72 02 0f 0b
|
||||
trapff ge v1, user0 ; bin: 72 02 user0 0f 0b
|
||||
; asm: jnb .+4; ud2
|
||||
trapff ult v1, user0 ; bin: 73 02 0f 0b
|
||||
trapff ult v1, user0 ; bin: 73 02 user0 0f 0b
|
||||
; asm: jnbe .+4; ud2
|
||||
trapff ule v1, user0 ; bin: 77 02 0f 0b
|
||||
trapff ule v1, user0 ; bin: 77 02 user0 0f 0b
|
||||
|
||||
; asm: setnp %bl
|
||||
[-,%rbx] v10 = trueff ord v1 ; bin: 0f 9b c3
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; binary emission of 32-bit code.
|
||||
; binary emission of x86-32 code.
|
||||
test binemit
|
||||
set is_compressed
|
||||
isa intel haswell
|
||||
@@ -25,6 +25,9 @@ ebb0:
|
||||
; asm: movl $2, %esi
|
||||
[-,%rsi] v2 = iconst.i32 2 ; bin: be 00000002
|
||||
|
||||
; asm: movb $1, %cl
|
||||
[-,%rcx] v9007 = bconst.b1 true ; bin: b9 00000001
|
||||
|
||||
; Integer Register-Register Operations.
|
||||
|
||||
; asm: addl %esi, %ecx
|
||||
@@ -125,13 +128,13 @@ ebb0:
|
||||
; asm: movl $2, %edx
|
||||
[-,%rdx] v53 = iconst.i32 2 ; bin: ba 00000002
|
||||
; asm: idivl %ecx
|
||||
[-,%rax,%rdx] v54, v55 = x86_sdivmodx v52, v53, v1 ; bin: f7 f9
|
||||
[-,%rax,%rdx] v54, v55 = x86_sdivmodx v52, v53, v1 ; bin: int_divz f7 f9
|
||||
; asm: idivl %esi
|
||||
[-,%rax,%rdx] v56, v57 = x86_sdivmodx v52, v53, v2 ; bin: f7 fe
|
||||
[-,%rax,%rdx] v56, v57 = x86_sdivmodx v52, v53, v2 ; bin: int_divz f7 fe
|
||||
; asm: divl %ecx
|
||||
[-,%rax,%rdx] v58, v59 = x86_udivmodx v52, v53, v1 ; bin: f7 f1
|
||||
[-,%rax,%rdx] v58, v59 = x86_udivmodx v52, v53, v1 ; bin: int_divz f7 f1
|
||||
; asm: divl %esi
|
||||
[-,%rax,%rdx] v60, v61 = x86_udivmodx v52, v53, v2 ; bin: f7 f6
|
||||
[-,%rax,%rdx] v60, v61 = x86_udivmodx v52, v53, v2 ; bin: int_divz f7 f6
|
||||
|
||||
; Register copies.
|
||||
|
||||
@@ -152,105 +155,105 @@ ebb0:
|
||||
; Register indirect addressing with no displacement.
|
||||
|
||||
; asm: movl %ecx, (%esi)
|
||||
store v1, v2 ; bin: 89 0e
|
||||
store v1, v2 ; bin: heap_oob 89 0e
|
||||
; asm: movl %esi, (%ecx)
|
||||
store v2, v1 ; bin: 89 31
|
||||
store v2, v1 ; bin: heap_oob 89 31
|
||||
; asm: movw %cx, (%esi)
|
||||
istore16 v1, v2 ; bin: 66 89 0e
|
||||
istore16 v1, v2 ; bin: heap_oob 66 89 0e
|
||||
; asm: movw %si, (%ecx)
|
||||
istore16 v2, v1 ; bin: 66 89 31
|
||||
istore16 v2, v1 ; bin: heap_oob 66 89 31
|
||||
; asm: movb %cl, (%esi)
|
||||
istore8 v1, v2 ; bin: 88 0e
|
||||
istore8 v1, v2 ; bin: heap_oob 88 0e
|
||||
; Can't store %sil in 32-bit mode (needs REX prefix).
|
||||
|
||||
; asm: movl (%ecx), %edi
|
||||
[-,%rdi] v100 = load.i32 v1 ; bin: 8b 39
|
||||
[-,%rdi] v100 = load.i32 v1 ; bin: heap_oob 8b 39
|
||||
; asm: movl (%esi), %edx
|
||||
[-,%rdx] v101 = load.i32 v2 ; bin: 8b 16
|
||||
[-,%rdx] v101 = load.i32 v2 ; bin: heap_oob 8b 16
|
||||
; asm: movzwl (%ecx), %edi
|
||||
[-,%rdi] v102 = uload16.i32 v1 ; bin: 0f b7 39
|
||||
[-,%rdi] v102 = uload16.i32 v1 ; bin: heap_oob 0f b7 39
|
||||
; asm: movzwl (%esi), %edx
|
||||
[-,%rdx] v103 = uload16.i32 v2 ; bin: 0f b7 16
|
||||
[-,%rdx] v103 = uload16.i32 v2 ; bin: heap_oob 0f b7 16
|
||||
; asm: movswl (%ecx), %edi
|
||||
[-,%rdi] v104 = sload16.i32 v1 ; bin: 0f bf 39
|
||||
[-,%rdi] v104 = sload16.i32 v1 ; bin: heap_oob 0f bf 39
|
||||
; asm: movswl (%esi), %edx
|
||||
[-,%rdx] v105 = sload16.i32 v2 ; bin: 0f bf 16
|
||||
[-,%rdx] v105 = sload16.i32 v2 ; bin: heap_oob 0f bf 16
|
||||
; asm: movzbl (%ecx), %edi
|
||||
[-,%rdi] v106 = uload8.i32 v1 ; bin: 0f b6 39
|
||||
[-,%rdi] v106 = uload8.i32 v1 ; bin: heap_oob 0f b6 39
|
||||
; asm: movzbl (%esi), %edx
|
||||
[-,%rdx] v107 = uload8.i32 v2 ; bin: 0f b6 16
|
||||
[-,%rdx] v107 = uload8.i32 v2 ; bin: heap_oob 0f b6 16
|
||||
; asm: movsbl (%ecx), %edi
|
||||
[-,%rdi] v108 = sload8.i32 v1 ; bin: 0f be 39
|
||||
[-,%rdi] v108 = sload8.i32 v1 ; bin: heap_oob 0f be 39
|
||||
; asm: movsbl (%esi), %edx
|
||||
[-,%rdx] v109 = sload8.i32 v2 ; bin: 0f be 16
|
||||
[-,%rdx] v109 = sload8.i32 v2 ; bin: heap_oob 0f be 16
|
||||
|
||||
; Register-indirect with 8-bit signed displacement.
|
||||
|
||||
; asm: movl %ecx, 100(%esi)
|
||||
store v1, v2+100 ; bin: 89 4e 64
|
||||
store v1, v2+100 ; bin: heap_oob 89 4e 64
|
||||
; asm: movl %esi, -100(%ecx)
|
||||
store v2, v1-100 ; bin: 89 71 9c
|
||||
store v2, v1-100 ; bin: heap_oob 89 71 9c
|
||||
; asm: movw %cx, 100(%esi)
|
||||
istore16 v1, v2+100 ; bin: 66 89 4e 64
|
||||
istore16 v1, v2+100 ; bin: heap_oob 66 89 4e 64
|
||||
; asm: movw %si, -100(%ecx)
|
||||
istore16 v2, v1-100 ; bin: 66 89 71 9c
|
||||
istore16 v2, v1-100 ; bin: heap_oob 66 89 71 9c
|
||||
; asm: movb %cl, 100(%esi)
|
||||
istore8 v1, v2+100 ; bin: 88 4e 64
|
||||
istore8 v1, v2+100 ; bin: heap_oob 88 4e 64
|
||||
|
||||
; asm: movl 50(%ecx), %edi
|
||||
[-,%rdi] v110 = load.i32 v1+50 ; bin: 8b 79 32
|
||||
[-,%rdi] v110 = load.i32 v1+50 ; bin: heap_oob 8b 79 32
|
||||
; asm: movl -50(%esi), %edx
|
||||
[-,%rdx] v111 = load.i32 v2-50 ; bin: 8b 56 ce
|
||||
[-,%rdx] v111 = load.i32 v2-50 ; bin: heap_oob 8b 56 ce
|
||||
; asm: movzwl 50(%ecx), %edi
|
||||
[-,%rdi] v112 = uload16.i32 v1+50 ; bin: 0f b7 79 32
|
||||
[-,%rdi] v112 = uload16.i32 v1+50 ; bin: heap_oob 0f b7 79 32
|
||||
; asm: movzwl -50(%esi), %edx
|
||||
[-,%rdx] v113 = uload16.i32 v2-50 ; bin: 0f b7 56 ce
|
||||
[-,%rdx] v113 = uload16.i32 v2-50 ; bin: heap_oob 0f b7 56 ce
|
||||
; asm: movswl 50(%ecx), %edi
|
||||
[-,%rdi] v114 = sload16.i32 v1+50 ; bin: 0f bf 79 32
|
||||
[-,%rdi] v114 = sload16.i32 v1+50 ; bin: heap_oob 0f bf 79 32
|
||||
; asm: movswl -50(%esi), %edx
|
||||
[-,%rdx] v115 = sload16.i32 v2-50 ; bin: 0f bf 56 ce
|
||||
[-,%rdx] v115 = sload16.i32 v2-50 ; bin: heap_oob 0f bf 56 ce
|
||||
; asm: movzbl 50(%ecx), %edi
|
||||
[-,%rdi] v116 = uload8.i32 v1+50 ; bin: 0f b6 79 32
|
||||
[-,%rdi] v116 = uload8.i32 v1+50 ; bin: heap_oob 0f b6 79 32
|
||||
; asm: movzbl -50(%esi), %edx
|
||||
[-,%rdx] v117 = uload8.i32 v2-50 ; bin: 0f b6 56 ce
|
||||
[-,%rdx] v117 = uload8.i32 v2-50 ; bin: heap_oob 0f b6 56 ce
|
||||
; asm: movsbl 50(%ecx), %edi
|
||||
[-,%rdi] v118 = sload8.i32 v1+50 ; bin: 0f be 79 32
|
||||
[-,%rdi] v118 = sload8.i32 v1+50 ; bin: heap_oob 0f be 79 32
|
||||
; asm: movsbl -50(%esi), %edx
|
||||
[-,%rdx] v119 = sload8.i32 v2-50 ; bin: 0f be 56 ce
|
||||
[-,%rdx] v119 = sload8.i32 v2-50 ; bin: heap_oob 0f be 56 ce
|
||||
|
||||
; Register-indirect with 32-bit signed displacement.
|
||||
|
||||
; asm: movl %ecx, 10000(%esi)
|
||||
store v1, v2+10000 ; bin: 89 8e 00002710
|
||||
store v1, v2+10000 ; bin: heap_oob 89 8e 00002710
|
||||
; asm: movl %esi, -10000(%ecx)
|
||||
store v2, v1-10000 ; bin: 89 b1 ffffd8f0
|
||||
store v2, v1-10000 ; bin: heap_oob 89 b1 ffffd8f0
|
||||
; asm: movw %cx, 10000(%esi)
|
||||
istore16 v1, v2+10000 ; bin: 66 89 8e 00002710
|
||||
istore16 v1, v2+10000 ; bin: heap_oob 66 89 8e 00002710
|
||||
; asm: movw %si, -10000(%ecx)
|
||||
istore16 v2, v1-10000 ; bin: 66 89 b1 ffffd8f0
|
||||
istore16 v2, v1-10000 ; bin: heap_oob 66 89 b1 ffffd8f0
|
||||
; asm: movb %cl, 10000(%esi)
|
||||
istore8 v1, v2+10000 ; bin: 88 8e 00002710
|
||||
istore8 v1, v2+10000 ; bin: heap_oob 88 8e 00002710
|
||||
|
||||
; asm: movl 50000(%ecx), %edi
|
||||
[-,%rdi] v120 = load.i32 v1+50000 ; bin: 8b b9 0000c350
|
||||
[-,%rdi] v120 = load.i32 v1+50000 ; bin: heap_oob 8b b9 0000c350
|
||||
; asm: movl -50000(%esi), %edx
|
||||
[-,%rdx] v121 = load.i32 v2-50000 ; bin: 8b 96 ffff3cb0
|
||||
[-,%rdx] v121 = load.i32 v2-50000 ; bin: heap_oob 8b 96 ffff3cb0
|
||||
; asm: movzwl 50000(%ecx), %edi
|
||||
[-,%rdi] v122 = uload16.i32 v1+50000 ; bin: 0f b7 b9 0000c350
|
||||
[-,%rdi] v122 = uload16.i32 v1+50000 ; bin: heap_oob 0f b7 b9 0000c350
|
||||
; asm: movzwl -50000(%esi), %edx
|
||||
[-,%rdx] v123 = uload16.i32 v2-50000 ; bin: 0f b7 96 ffff3cb0
|
||||
[-,%rdx] v123 = uload16.i32 v2-50000 ; bin: heap_oob 0f b7 96 ffff3cb0
|
||||
; asm: movswl 50000(%ecx), %edi
|
||||
[-,%rdi] v124 = sload16.i32 v1+50000 ; bin: 0f bf b9 0000c350
|
||||
[-,%rdi] v124 = sload16.i32 v1+50000 ; bin: heap_oob 0f bf b9 0000c350
|
||||
; asm: movswl -50000(%esi), %edx
|
||||
[-,%rdx] v125 = sload16.i32 v2-50000 ; bin: 0f bf 96 ffff3cb0
|
||||
[-,%rdx] v125 = sload16.i32 v2-50000 ; bin: heap_oob 0f bf 96 ffff3cb0
|
||||
; asm: movzbl 50000(%ecx), %edi
|
||||
[-,%rdi] v126 = uload8.i32 v1+50000 ; bin: 0f b6 b9 0000c350
|
||||
[-,%rdi] v126 = uload8.i32 v1+50000 ; bin: heap_oob 0f b6 b9 0000c350
|
||||
; asm: movzbl -50000(%esi), %edx
|
||||
[-,%rdx] v127 = uload8.i32 v2-50000 ; bin: 0f b6 96 ffff3cb0
|
||||
[-,%rdx] v127 = uload8.i32 v2-50000 ; bin: heap_oob 0f b6 96 ffff3cb0
|
||||
; asm: movsbl 50000(%ecx), %edi
|
||||
[-,%rdi] v128 = sload8.i32 v1+50000 ; bin: 0f be b9 0000c350
|
||||
[-,%rdi] v128 = sload8.i32 v1+50000 ; bin: heap_oob 0f be b9 0000c350
|
||||
; asm: movsbl -50000(%esi), %edx
|
||||
[-,%rdx] v129 = sload8.i32 v2-50000 ; bin: 0f be 96 ffff3cb0
|
||||
[-,%rdx] v129 = sload8.i32 v2-50000 ; bin: heap_oob 0f be 96 ffff3cb0
|
||||
|
||||
; Bit-counting instructions.
|
||||
|
||||
@@ -403,6 +406,13 @@ ebb0:
|
||||
; asm: addl $-2147483648, %esp
|
||||
adjust_sp_imm -2147483648 ; bin: 81 c4 80000000
|
||||
|
||||
; Shift immediates
|
||||
; asm: shll $2, %esi
|
||||
[-,%rsi] v513 = ishl_imm v2, 2 ; bin: c1 e6 02
|
||||
; asm: sarl $5, %esi
|
||||
[-,%rsi] v514 = sshr_imm v2, 5 ; bin: c1 fe 05
|
||||
; asm: shrl $8, %esi
|
||||
[-,%rsi] v515 = ushr_imm v2, 8 ; bin: c1 ee 08
|
||||
|
||||
; asm: testl %ecx, %ecx
|
||||
; asm: je ebb1
|
||||
@@ -427,7 +437,7 @@ ebb1:
|
||||
|
||||
; asm: ebb2:
|
||||
ebb2:
|
||||
trap user0 ; bin: 0f 0b
|
||||
trap user0 ; bin: user0 0f 0b
|
||||
}
|
||||
|
||||
; Special branch encodings only for I32 mode.
|
||||
@@ -466,9 +476,9 @@ ebb0:
|
||||
|
||||
ebb1:
|
||||
; asm: cmpl %esi, %ecx
|
||||
[-,%eflags] v10 = ifcmp v1, v2 ; bin: 39 f1
|
||||
[-,%rflags] v10 = ifcmp v1, v2 ; bin: 39 f1
|
||||
; asm: cmpl %ecx, %esi
|
||||
[-,%eflags] v11 = ifcmp v2, v1 ; bin: 39 ce
|
||||
[-,%rflags] v11 = ifcmp v2, v1 ; bin: 39 ce
|
||||
|
||||
; asm: je ebb1
|
||||
brif eq v11, ebb1 ; bin: 74 fa
|
||||
@@ -514,41 +524,41 @@ ebb1:
|
||||
|
||||
; The trapif instructions are encoded as macros: a conditional jump over a ud2.
|
||||
; asm: jne .+4; ud2
|
||||
trapif eq v11, user0 ; bin: 75 02 0f 0b
|
||||
trapif eq v11, user0 ; bin: 75 02 user0 0f 0b
|
||||
; asm: je .+4; ud2
|
||||
trapif ne v11, user0 ; bin: 74 02 0f 0b
|
||||
trapif ne v11, user0 ; bin: 74 02 user0 0f 0b
|
||||
; asm: jnl .+4; ud2
|
||||
trapif slt v11, user0 ; bin: 7d 02 0f 0b
|
||||
trapif slt v11, user0 ; bin: 7d 02 user0 0f 0b
|
||||
; asm: jnge .+4; ud2
|
||||
trapif sge v11, user0 ; bin: 7c 02 0f 0b
|
||||
trapif sge v11, user0 ; bin: 7c 02 user0 0f 0b
|
||||
; asm: jng .+4; ud2
|
||||
trapif sgt v11, user0 ; bin: 7e 02 0f 0b
|
||||
trapif sgt v11, user0 ; bin: 7e 02 user0 0f 0b
|
||||
; asm: jnle .+4; ud2
|
||||
trapif sle v11, user0 ; bin: 7f 02 0f 0b
|
||||
trapif sle v11, user0 ; bin: 7f 02 user0 0f 0b
|
||||
; asm: jnb .+4; ud2
|
||||
trapif ult v11, user0 ; bin: 73 02 0f 0b
|
||||
trapif ult v11, user0 ; bin: 73 02 user0 0f 0b
|
||||
; asm: jnae .+4; ud2
|
||||
trapif uge v11, user0 ; bin: 72 02 0f 0b
|
||||
trapif uge v11, user0 ; bin: 72 02 user0 0f 0b
|
||||
; asm: jna .+4; ud2
|
||||
trapif ugt v11, user0 ; bin: 76 02 0f 0b
|
||||
trapif ugt v11, user0 ; bin: 76 02 user0 0f 0b
|
||||
; asm: jnbe .+4; ud2
|
||||
trapif ule v11, user0 ; bin: 77 02 0f 0b
|
||||
trapif ule v11, user0 ; bin: 77 02 user0 0f 0b
|
||||
|
||||
; Stack check.
|
||||
; asm: cmpl %esp, %ecx
|
||||
[-,%eflags] v40 = ifcmp_sp v1 ; bin: 39 e1
|
||||
[-,%rflags] v40 = ifcmp_sp v1 ; bin: 39 e1
|
||||
; asm: cmpl %esp, %esi
|
||||
[-,%eflags] v41 = ifcmp_sp v2 ; bin: 39 e6
|
||||
[-,%rflags] v41 = ifcmp_sp v2 ; bin: 39 e6
|
||||
|
||||
; asm: cmpl $-100, %ecx
|
||||
[-,%eflags] v42 = ifcmp_imm v1, -100 ; bin: 83 f9 9c
|
||||
[-,%rflags] v42 = ifcmp_imm v1, -100 ; bin: 83 f9 9c
|
||||
; asm: cmpl $100, %esi
|
||||
[-,%eflags] v43 = ifcmp_imm v2, 100 ; bin: 83 fe 64
|
||||
[-,%rflags] v43 = ifcmp_imm v2, 100 ; bin: 83 fe 64
|
||||
|
||||
; asm: cmpl $-10000, %ecx
|
||||
[-,%eflags] v44 = ifcmp_imm v1, -10000 ; bin: 81 f9 ffffd8f0
|
||||
[-,%rflags] v44 = ifcmp_imm v1, -10000 ; bin: 81 f9 ffffd8f0
|
||||
; asm: cmpl $10000, %esi
|
||||
[-,%eflags] v45 = ifcmp_imm v2, 10000 ; bin: 81 fe 00002710
|
||||
[-,%rflags] v45 = ifcmp_imm v2, 10000 ; bin: 81 fe 00002710
|
||||
|
||||
return
|
||||
}
|
||||
@@ -566,7 +576,7 @@ ebb0:
|
||||
; asm: movzbl %cl, %esi
|
||||
[-,%rsi] v30 = uextend.i32 v11 ; bin: 0f b6 f1
|
||||
|
||||
trap user0 ; bin: 0f 0b
|
||||
trap user0 ; bin: user0 0f 0b
|
||||
}
|
||||
|
||||
; Tests for i32/i16 conversion instructions.
|
||||
@@ -582,5 +592,5 @@ ebb0:
|
||||
; asm: movzwl %cx, %esi
|
||||
[-,%rsi] v30 = uextend.i32 v11 ; bin: 0f b7 f1
|
||||
|
||||
trap user0 ; bin: 0f 0b
|
||||
trap user0 ; bin: user0 0f 0b
|
||||
}
|
||||
|
||||
@@ -157,52 +157,52 @@ ebb0:
|
||||
|
||||
; Load/Store
|
||||
|
||||
; asm: movd (%r14), %xmm5
|
||||
[-,%xmm5] v100 = load.f32 v3 ; bin: 66 41 0f 6e 2e
|
||||
; asm: movd (%rax), %xmm10
|
||||
[-,%xmm10] v101 = load.f32 v2 ; bin: 66 44 0f 6e 10
|
||||
; asm: movd 50(%r14), %xmm5
|
||||
[-,%xmm5] v110 = load.f32 v3+50 ; bin: 66 41 0f 6e 6e 32
|
||||
; asm: movd -50(%rax), %xmm10
|
||||
[-,%xmm10] v111 = load.f32 v2-50 ; bin: 66 44 0f 6e 50 ce
|
||||
; asm: movd 10000(%r14), %xmm5
|
||||
[-,%xmm5] v120 = load.f32 v3+10000 ; bin: 66 41 0f 6e ae 00002710
|
||||
; asm: movd -10000(%rax), %xmm10
|
||||
[-,%xmm10] v121 = load.f32 v2-10000 ; bin: 66 44 0f 6e 90 ffffd8f0
|
||||
; asm: movss (%r14), %xmm5
|
||||
[-,%xmm5] v100 = load.f32 v3 ; bin: heap_oob f3 41 0f 10 2e
|
||||
; asm: movss (%rax), %xmm10
|
||||
[-,%xmm10] v101 = load.f32 v2 ; bin: heap_oob f3 44 0f 10 10
|
||||
; asm: movss 50(%r14), %xmm5
|
||||
[-,%xmm5] v110 = load.f32 v3+50 ; bin: heap_oob f3 41 0f 10 6e 32
|
||||
; asm: movss -50(%rax), %xmm10
|
||||
[-,%xmm10] v111 = load.f32 v2-50 ; bin: heap_oob f3 44 0f 10 50 ce
|
||||
; asm: movss 10000(%r14), %xmm5
|
||||
[-,%xmm5] v120 = load.f32 v3+10000 ; bin: heap_oob f3 41 0f 10 ae 00002710
|
||||
; asm: movss -10000(%rax), %xmm10
|
||||
[-,%xmm10] v121 = load.f32 v2-10000 ; bin: heap_oob f3 44 0f 10 90 ffffd8f0
|
||||
|
||||
; asm: movd %xmm5, (%r14)
|
||||
[-] store.f32 v100, v3 ; bin: 66 41 0f 7e 2e
|
||||
; asm: movd %xmm10, (%rax)
|
||||
[-] store.f32 v101, v2 ; bin: 66 44 0f 7e 10
|
||||
; asm: movd %xmm5, (%r13)
|
||||
[-] store.f32 v100, v4 ; bin: 66 41 0f 7e 6d 00
|
||||
; asm: movd %xmm10, (%r13)
|
||||
[-] store.f32 v101, v4 ; bin: 66 45 0f 7e 55 00
|
||||
; asm: movd %xmm5, 50(%r14)
|
||||
[-] store.f32 v100, v3+50 ; bin: 66 41 0f 7e 6e 32
|
||||
; asm: movd %xmm10, -50(%rax)
|
||||
[-] store.f32 v101, v2-50 ; bin: 66 44 0f 7e 50 ce
|
||||
; asm: movd %xmm5, 10000(%r14)
|
||||
[-] store.f32 v100, v3+10000 ; bin: 66 41 0f 7e ae 00002710
|
||||
; asm: movd %xmm10, -10000(%rax)
|
||||
[-] store.f32 v101, v2-10000 ; bin: 66 44 0f 7e 90 ffffd8f0
|
||||
; asm: movss %xmm5, (%r14)
|
||||
[-] store.f32 v100, v3 ; bin: heap_oob f3 41 0f 11 2e
|
||||
; asm: movss %xmm10, (%rax)
|
||||
[-] store.f32 v101, v2 ; bin: heap_oob f3 44 0f 11 10
|
||||
; asm: movss %xmm5, (%r13)
|
||||
[-] store.f32 v100, v4 ; bin: heap_oob f3 41 0f 11 6d 00
|
||||
; asm: movss %xmm10, (%r13)
|
||||
[-] store.f32 v101, v4 ; bin: heap_oob f3 45 0f 11 55 00
|
||||
; asm: movss %xmm5, 50(%r14)
|
||||
[-] store.f32 v100, v3+50 ; bin: heap_oob f3 41 0f 11 6e 32
|
||||
; asm: movss %xmm10, -50(%rax)
|
||||
[-] store.f32 v101, v2-50 ; bin: heap_oob f3 44 0f 11 50 ce
|
||||
; asm: movss %xmm5, 10000(%r14)
|
||||
[-] store.f32 v100, v3+10000 ; bin: heap_oob f3 41 0f 11 ae 00002710
|
||||
; asm: movss %xmm10, -10000(%rax)
|
||||
[-] store.f32 v101, v2-10000 ; bin: heap_oob f3 44 0f 11 90 ffffd8f0
|
||||
|
||||
; Spill / Fill.
|
||||
|
||||
; asm: movd %xmm5, 1032(%rsp)
|
||||
[-,ss1] v200 = spill v100 ; bin: 66 0f 7e ac 24 00000408
|
||||
; asm: movd %xmm10, 1032(%rsp)
|
||||
[-,ss1] v201 = spill v101 ; bin: 66 44 0f 7e 94 24 00000408
|
||||
; asm: movss %xmm5, 1032(%rsp)
|
||||
[-,ss1] v200 = spill v100 ; bin: f3 0f 11 ac 24 00000408
|
||||
; asm: movss %xmm10, 1032(%rsp)
|
||||
[-,ss1] v201 = spill v101 ; bin: f3 44 0f 11 94 24 00000408
|
||||
|
||||
; asm: movd 1032(%rsp), %xmm5
|
||||
[-,%xmm5] v210 = fill v200 ; bin: 66 0f 6e ac 24 00000408
|
||||
; asm: movd 1032(%rsp), %xmm10
|
||||
[-,%xmm10] v211 = fill v201 ; bin: 66 44 0f 6e 94 24 00000408
|
||||
; asm: movss 1032(%rsp), %xmm5
|
||||
[-,%xmm5] v210 = fill v200 ; bin: f3 0f 10 ac 24 00000408
|
||||
; asm: movss 1032(%rsp), %xmm10
|
||||
[-,%xmm10] v211 = fill v201 ; bin: f3 44 0f 10 94 24 00000408
|
||||
|
||||
; asm: movd %xmm5, 1032(%rsp)
|
||||
regspill v100, %xmm5 -> ss1 ; bin: 66 0f 7e ac 24 00000408
|
||||
; asm: movd 1032(%rsp), %xmm5
|
||||
regfill v100, ss1 -> %xmm5 ; bin: 66 0f 6e ac 24 00000408
|
||||
; asm: movss %xmm5, 1032(%rsp)
|
||||
regspill v100, %xmm5 -> ss1 ; bin: f3 0f 11 ac 24 00000408
|
||||
; asm: movss 1032(%rsp), %xmm5
|
||||
regfill v100, ss1 -> %xmm5 ; bin: f3 0f 10 ac 24 00000408
|
||||
|
||||
; Comparisons.
|
||||
;
|
||||
@@ -235,11 +235,11 @@ ebb0:
|
||||
[-,%rdx] v307 = fcmp ule v11, v10 ; bin: 44 0f 2e d5 0f 96 c2
|
||||
|
||||
; asm: ucomiss %xmm10, %xmm5
|
||||
[-,%eflags] v310 = ffcmp v10, v11 ; bin: 41 0f 2e ea
|
||||
[-,%rflags] v310 = ffcmp v10, v11 ; bin: 41 0f 2e ea
|
||||
; asm: ucomiss %xmm10, %xmm5
|
||||
[-,%eflags] v311 = ffcmp v11, v10 ; bin: 44 0f 2e d5
|
||||
[-,%rflags] v311 = ffcmp v11, v10 ; bin: 44 0f 2e d5
|
||||
; asm: ucomiss %xmm5, %xmm5
|
||||
[-,%eflags] v312 = ffcmp v10, v10 ; bin: 0f 2e ed
|
||||
[-,%rflags] v312 = ffcmp v10, v10 ; bin: 0f 2e ed
|
||||
|
||||
return
|
||||
}
|
||||
@@ -392,52 +392,52 @@ ebb0:
|
||||
|
||||
; Load/Store
|
||||
|
||||
; asm: movq (%r14), %xmm5
|
||||
[-,%xmm5] v100 = load.f64 v3 ; bin: f3 41 0f 7e 2e
|
||||
; asm: movq (%rax), %xmm10
|
||||
[-,%xmm10] v101 = load.f64 v2 ; bin: f3 44 0f 7e 10
|
||||
; asm: movq 50(%r14), %xmm5
|
||||
[-,%xmm5] v110 = load.f64 v3+50 ; bin: f3 41 0f 7e 6e 32
|
||||
; asm: movq -50(%rax), %xmm10
|
||||
[-,%xmm10] v111 = load.f64 v2-50 ; bin: f3 44 0f 7e 50 ce
|
||||
; asm: movq 10000(%r14), %xmm5
|
||||
[-,%xmm5] v120 = load.f64 v3+10000 ; bin: f3 41 0f 7e ae 00002710
|
||||
; asm: movq -10000(%rax), %xmm10
|
||||
[-,%xmm10] v121 = load.f64 v2-10000 ; bin: f3 44 0f 7e 90 ffffd8f0
|
||||
; asm: movsd (%r14), %xmm5
|
||||
[-,%xmm5] v100 = load.f64 v3 ; bin: heap_oob f2 41 0f 10 2e
|
||||
; asm: movsd (%rax), %xmm10
|
||||
[-,%xmm10] v101 = load.f64 v2 ; bin: heap_oob f2 44 0f 10 10
|
||||
; asm: movsd 50(%r14), %xmm5
|
||||
[-,%xmm5] v110 = load.f64 v3+50 ; bin: heap_oob f2 41 0f 10 6e 32
|
||||
; asm: movsd -50(%rax), %xmm10
|
||||
[-,%xmm10] v111 = load.f64 v2-50 ; bin: heap_oob f2 44 0f 10 50 ce
|
||||
; asm: movsd 10000(%r14), %xmm5
|
||||
[-,%xmm5] v120 = load.f64 v3+10000 ; bin: heap_oob f2 41 0f 10 ae 00002710
|
||||
; asm: movsd -10000(%rax), %xmm10
|
||||
[-,%xmm10] v121 = load.f64 v2-10000 ; bin: heap_oob f2 44 0f 10 90 ffffd8f0
|
||||
|
||||
; asm: movq %xmm5, (%r14)
|
||||
[-] store.f64 v100, v3 ; bin: 66 41 0f d6 2e
|
||||
; asm: movq %xmm10, (%rax)
|
||||
[-] store.f64 v101, v2 ; bin: 66 44 0f d6 10
|
||||
; asm: movq %xmm5, (%r13)
|
||||
[-] store.f64 v100, v4 ; bin: 66 41 0f d6 6d 00
|
||||
; asm: movq %xmm10, (%r13)
|
||||
[-] store.f64 v101, v4 ; bin: 66 45 0f d6 55 00
|
||||
; asm: movq %xmm5, 50(%r14)
|
||||
[-] store.f64 v100, v3+50 ; bin: 66 41 0f d6 6e 32
|
||||
; asm: movq %xmm10, -50(%rax)
|
||||
[-] store.f64 v101, v2-50 ; bin: 66 44 0f d6 50 ce
|
||||
; asm: movq %xmm5, 10000(%r14)
|
||||
[-] store.f64 v100, v3+10000 ; bin: 66 41 0f d6 ae 00002710
|
||||
; asm: movq %xmm10, -10000(%rax)
|
||||
[-] store.f64 v101, v2-10000 ; bin: 66 44 0f d6 90 ffffd8f0
|
||||
; asm: movsd %xmm5, (%r14)
|
||||
[-] store.f64 v100, v3 ; bin: heap_oob f2 41 0f 11 2e
|
||||
; asm: movsd %xmm10, (%rax)
|
||||
[-] store.f64 v101, v2 ; bin: heap_oob f2 44 0f 11 10
|
||||
; asm: movsd %xmm5, (%r13)
|
||||
[-] store.f64 v100, v4 ; bin: heap_oob f2 41 0f 11 6d 00
|
||||
; asm: movsd %xmm10, (%r13)
|
||||
[-] store.f64 v101, v4 ; bin: heap_oob f2 45 0f 11 55 00
|
||||
; asm: movsd %xmm5, 50(%r14)
|
||||
[-] store.f64 v100, v3+50 ; bin: heap_oob f2 41 0f 11 6e 32
|
||||
; asm: movsd %xmm10, -50(%rax)
|
||||
[-] store.f64 v101, v2-50 ; bin: heap_oob f2 44 0f 11 50 ce
|
||||
; asm: movsd %xmm5, 10000(%r14)
|
||||
[-] store.f64 v100, v3+10000 ; bin: heap_oob f2 41 0f 11 ae 00002710
|
||||
; asm: movsd %xmm10, -10000(%rax)
|
||||
[-] store.f64 v101, v2-10000 ; bin: heap_oob f2 44 0f 11 90 ffffd8f0
|
||||
|
||||
; Spill / Fill.
|
||||
|
||||
; asm: movq %xmm5, 1032(%rsp)
|
||||
[-,ss1] v200 = spill v100 ; bin: 66 0f d6 ac 24 00000408
|
||||
; asm: movq %xmm10, 1032(%rsp)
|
||||
[-,ss1] v201 = spill v101 ; bin: 66 44 0f d6 94 24 00000408
|
||||
; asm: movsd %xmm5, 1032(%rsp)
|
||||
[-,ss1] v200 = spill v100 ; bin: f2 0f 11 ac 24 00000408
|
||||
; asm: movsd %xmm10, 1032(%rsp)
|
||||
[-,ss1] v201 = spill v101 ; bin: f2 44 0f 11 94 24 00000408
|
||||
|
||||
; asm: movq 1032(%rsp), %xmm5
|
||||
[-,%xmm5] v210 = fill v200 ; bin: f3 0f 7e ac 24 00000408
|
||||
; asm: movq 1032(%rsp), %xmm10
|
||||
[-,%xmm10] v211 = fill v201 ; bin: f3 44 0f 7e 94 24 00000408
|
||||
; asm: movsd 1032(%rsp), %xmm5
|
||||
[-,%xmm5] v210 = fill v200 ; bin: f2 0f 10 ac 24 00000408
|
||||
; asm: movsd 1032(%rsp), %xmm10
|
||||
[-,%xmm10] v211 = fill v201 ; bin: f2 44 0f 10 94 24 00000408
|
||||
|
||||
; asm: movq %xmm5, 1032(%rsp)
|
||||
regspill v100, %xmm5 -> ss1 ; bin: 66 0f d6 ac 24 00000408
|
||||
; asm: movq 1032(%rsp), %xmm5
|
||||
regfill v100, ss1 -> %xmm5 ; bin: f3 0f 7e ac 24 00000408
|
||||
; asm: movsd %xmm5, 1032(%rsp)
|
||||
regspill v100, %xmm5 -> ss1 ; bin: f2 0f 11 ac 24 00000408
|
||||
; asm: movsd 1032(%rsp), %xmm5
|
||||
regfill v100, ss1 -> %xmm5 ; bin: f2 0f 10 ac 24 00000408
|
||||
|
||||
; Comparisons.
|
||||
;
|
||||
@@ -470,11 +470,11 @@ ebb0:
|
||||
[-,%rdx] v307 = fcmp ule v11, v10 ; bin: 66 44 0f 2e d5 0f 96 c2
|
||||
|
||||
; asm: ucomisd %xmm10, %xmm5
|
||||
[-,%eflags] v310 = ffcmp v10, v11 ; bin: 66 41 0f 2e ea
|
||||
[-,%rflags] v310 = ffcmp v10, v11 ; bin: 66 41 0f 2e ea
|
||||
; asm: ucomisd %xmm10, %xmm5
|
||||
[-,%eflags] v311 = ffcmp v11, v10 ; bin: 66 44 0f 2e d5
|
||||
[-,%rflags] v311 = ffcmp v11, v10 ; bin: 66 44 0f 2e d5
|
||||
; asm: ucomisd %xmm5, %xmm5
|
||||
[-,%eflags] v312 = ffcmp v10, v10 ; bin: 66 0f 2e ed
|
||||
[-,%rflags] v312 = ffcmp v10, v10 ; bin: 66 0f 2e ed
|
||||
|
||||
return
|
||||
}
|
||||
@@ -482,7 +482,7 @@ ebb0:
|
||||
function %cpuflags_float(f32 [%xmm0]) {
|
||||
ebb0(v0: f32 [%xmm0]):
|
||||
; asm: ucomiss %xmm0, %xmm0
|
||||
[-,%eflags] v1 = ffcmp v0, v0 ; bin: 0f 2e c0
|
||||
[-,%rflags] v1 = ffcmp v0, v0 ; bin: 0f 2e c0
|
||||
|
||||
jump ebb1
|
||||
|
||||
@@ -505,21 +505,21 @@ ebb1:
|
||||
brff ule v1, ebb1 ; bin: 76 f0
|
||||
|
||||
; asm: jp .+4; ud2
|
||||
trapff ord v1, user0 ; bin: 7a 02 0f 0b
|
||||
trapff ord v1, user0 ; bin: 7a 02 user0 0f 0b
|
||||
; asm: jnp .+4; ud2
|
||||
trapff uno v1, user0 ; bin: 7b 02 0f 0b
|
||||
trapff uno v1, user0 ; bin: 7b 02 user0 0f 0b
|
||||
; asm: je .+4; ud2
|
||||
trapff one v1, user0 ; bin: 74 02 0f 0b
|
||||
trapff one v1, user0 ; bin: 74 02 user0 0f 0b
|
||||
; asm: jne .+4; ud2
|
||||
trapff ueq v1, user0 ; bin: 75 02 0f 0b
|
||||
trapff ueq v1, user0 ; bin: 75 02 user0 0f 0b
|
||||
; asm: jna .+4; ud2
|
||||
trapff gt v1, user0 ; bin: 76 02 0f 0b
|
||||
trapff gt v1, user0 ; bin: 76 02 user0 0f 0b
|
||||
; asm: jnae .+4; ud2
|
||||
trapff ge v1, user0 ; bin: 72 02 0f 0b
|
||||
trapff ge v1, user0 ; bin: 72 02 user0 0f 0b
|
||||
; asm: jnb .+4; ud2
|
||||
trapff ult v1, user0 ; bin: 73 02 0f 0b
|
||||
trapff ult v1, user0 ; bin: 73 02 user0 0f 0b
|
||||
; asm: jnbe .+4; ud2
|
||||
trapff ule v1, user0 ; bin: 77 02 0f 0b
|
||||
trapff ule v1, user0 ; bin: 77 02 user0 0f 0b
|
||||
|
||||
; asm: setnp %bl
|
||||
[-,%rbx] v10 = trueff ord v1 ; bin: 0f 9b c3
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; binary emission of 64-bit code.
|
||||
; binary emission of x86-64 code.
|
||||
test binemit
|
||||
set is_64bit
|
||||
set is_compressed
|
||||
@@ -38,6 +38,11 @@ ebb0:
|
||||
; asm: movq $0xffffffff88001122, %r14 # 32-bit sign-extended constant.
|
||||
[-,%r14] v5 = iconst.i64 0xffff_ffff_8800_1122 ; bin: 49 c7 c6 88001122
|
||||
|
||||
; asm: movb $1, %cl
|
||||
[-,%rcx] v9007 = bconst.b1 true ; bin: b9 00000001
|
||||
; asm: movb $1, %sil
|
||||
[-,%r10] v9008 = bconst.b1 true ; bin: 41 ba 00000001
|
||||
|
||||
; Integer Register-Register Operations.
|
||||
|
||||
; asm: addq %rsi, %rcx
|
||||
@@ -170,146 +175,146 @@ ebb0:
|
||||
; Register indirect addressing with no displacement.
|
||||
|
||||
; asm: movq %rcx, (%r10)
|
||||
store v1, v3 ; bin: 49 89 0a
|
||||
store v1, v3 ; bin: heap_oob 49 89 0a
|
||||
; asm: movq %r10, (%rcx)
|
||||
store v3, v1 ; bin: 4c 89 11
|
||||
store v3, v1 ; bin: heap_oob 4c 89 11
|
||||
; asm: movl %ecx, (%r10)
|
||||
istore32 v1, v3 ; bin: 41 89 0a
|
||||
istore32 v1, v3 ; bin: heap_oob 41 89 0a
|
||||
; asm: movl %r10d, (%rcx)
|
||||
istore32 v3, v1 ; bin: 44 89 11
|
||||
istore32 v3, v1 ; bin: heap_oob 44 89 11
|
||||
; asm: movw %cx, (%r10)
|
||||
istore16 v1, v3 ; bin: 66 41 89 0a
|
||||
istore16 v1, v3 ; bin: heap_oob 66 41 89 0a
|
||||
; asm: movw %r10w, (%rcx)
|
||||
istore16 v3, v1 ; bin: 66 44 89 11
|
||||
istore16 v3, v1 ; bin: heap_oob 66 44 89 11
|
||||
; asm: movb %cl, (%r10)
|
||||
istore8 v1, v3 ; bin: 41 88 0a
|
||||
istore8 v1, v3 ; bin: heap_oob 41 88 0a
|
||||
; asm: movb %r10b, (%rcx)
|
||||
istore8 v3, v1 ; bin: 44 88 11
|
||||
istore8 v3, v1 ; bin: heap_oob 44 88 11
|
||||
|
||||
; asm: movq (%rcx), %r14
|
||||
[-,%r14] v120 = load.i64 v1 ; bin: 4c 8b 31
|
||||
[-,%r14] v120 = load.i64 v1 ; bin: heap_oob 4c 8b 31
|
||||
; asm: movq (%r10), %rdx
|
||||
[-,%rdx] v121 = load.i64 v3 ; bin: 49 8b 12
|
||||
[-,%rdx] v121 = load.i64 v3 ; bin: heap_oob 49 8b 12
|
||||
; asm: movl (%rcx), %r14d
|
||||
[-,%r14] v122 = uload32.i64 v1 ; bin: 44 8b 31
|
||||
[-,%r14] v122 = uload32.i64 v1 ; bin: heap_oob 44 8b 31
|
||||
; asm: movl (%r10), %edx
|
||||
[-,%rdx] v123 = uload32.i64 v3 ; bin: 41 8b 12
|
||||
[-,%rdx] v123 = uload32.i64 v3 ; bin: heap_oob 41 8b 12
|
||||
; asm: movslq (%rcx), %r14
|
||||
[-,%r14] v124 = sload32.i64 v1 ; bin: 4c 63 31
|
||||
[-,%r14] v124 = sload32.i64 v1 ; bin: heap_oob 4c 63 31
|
||||
; asm: movslq (%r10), %rdx
|
||||
[-,%rdx] v125 = sload32.i64 v3 ; bin: 49 63 12
|
||||
[-,%rdx] v125 = sload32.i64 v3 ; bin: heap_oob 49 63 12
|
||||
; asm: movzwq (%rcx), %r14
|
||||
[-,%r14] v126 = uload16.i64 v1 ; bin: 4c 0f b7 31
|
||||
[-,%r14] v126 = uload16.i64 v1 ; bin: heap_oob 4c 0f b7 31
|
||||
; asm: movzwq (%r10), %rdx
|
||||
[-,%rdx] v127 = uload16.i64 v3 ; bin: 49 0f b7 12
|
||||
[-,%rdx] v127 = uload16.i64 v3 ; bin: heap_oob 49 0f b7 12
|
||||
; asm: movswq (%rcx), %r14
|
||||
[-,%r14] v128 = sload16.i64 v1 ; bin: 4c 0f bf 31
|
||||
[-,%r14] v128 = sload16.i64 v1 ; bin: heap_oob 4c 0f bf 31
|
||||
; asm: movswq (%r10), %rdx
|
||||
[-,%rdx] v129 = sload16.i64 v3 ; bin: 49 0f bf 12
|
||||
[-,%rdx] v129 = sload16.i64 v3 ; bin: heap_oob 49 0f bf 12
|
||||
; asm: movzbq (%rcx), %r14
|
||||
[-,%r14] v130 = uload8.i64 v1 ; bin: 4c 0f b6 31
|
||||
[-,%r14] v130 = uload8.i64 v1 ; bin: heap_oob 4c 0f b6 31
|
||||
; asm: movzbq (%r10), %rdx
|
||||
[-,%rdx] v131 = uload8.i64 v3 ; bin: 49 0f b6 12
|
||||
[-,%rdx] v131 = uload8.i64 v3 ; bin: heap_oob 49 0f b6 12
|
||||
; asm: movsbq (%rcx), %r14
|
||||
[-,%r14] v132 = sload8.i64 v1 ; bin: 4c 0f be 31
|
||||
[-,%r14] v132 = sload8.i64 v1 ; bin: heap_oob 4c 0f be 31
|
||||
; asm: movsbq (%r10), %rdx
|
||||
[-,%rdx] v133 = sload8.i64 v3 ; bin: 49 0f be 12
|
||||
[-,%rdx] v133 = sload8.i64 v3 ; bin: heap_oob 49 0f be 12
|
||||
|
||||
; Register-indirect with 8-bit signed displacement.
|
||||
|
||||
; asm: movq %rcx, 100(%r10)
|
||||
store v1, v3+100 ; bin: 49 89 4a 64
|
||||
store v1, v3+100 ; bin: heap_oob 49 89 4a 64
|
||||
; asm: movq %r10, -100(%rcx)
|
||||
store v3, v1-100 ; bin: 4c 89 51 9c
|
||||
store v3, v1-100 ; bin: heap_oob 4c 89 51 9c
|
||||
; asm: movl %ecx, 100(%r10)
|
||||
istore32 v1, v3+100 ; bin: 41 89 4a 64
|
||||
istore32 v1, v3+100 ; bin: heap_oob 41 89 4a 64
|
||||
; asm: movl %r10d, -100(%rcx)
|
||||
istore32 v3, v1-100 ; bin: 44 89 51 9c
|
||||
istore32 v3, v1-100 ; bin: heap_oob 44 89 51 9c
|
||||
; asm: movw %cx, 100(%r10)
|
||||
istore16 v1, v3+100 ; bin: 66 41 89 4a 64
|
||||
istore16 v1, v3+100 ; bin: heap_oob 66 41 89 4a 64
|
||||
; asm: movw %r10w, -100(%rcx)
|
||||
istore16 v3, v1-100 ; bin: 66 44 89 51 9c
|
||||
istore16 v3, v1-100 ; bin: heap_oob 66 44 89 51 9c
|
||||
; asm: movb %cl, 100(%r10)
|
||||
istore8 v1, v3+100 ; bin: 41 88 4a 64
|
||||
istore8 v1, v3+100 ; bin: heap_oob 41 88 4a 64
|
||||
; asm: movb %r10b, 100(%rcx)
|
||||
istore8 v3, v1+100 ; bin: 44 88 51 64
|
||||
istore8 v3, v1+100 ; bin: heap_oob 44 88 51 64
|
||||
|
||||
; asm: movq 50(%rcx), %r10
|
||||
[-,%r10] v140 = load.i64 v1+50 ; bin: 4c 8b 51 32
|
||||
[-,%r10] v140 = load.i64 v1+50 ; bin: heap_oob 4c 8b 51 32
|
||||
; asm: movq -50(%r10), %rdx
|
||||
[-,%rdx] v141 = load.i64 v3-50 ; bin: 49 8b 52 ce
|
||||
[-,%rdx] v141 = load.i64 v3-50 ; bin: heap_oob 49 8b 52 ce
|
||||
; asm: movl 50(%rcx), %edi
|
||||
[-,%rdi] v142 = uload32.i64 v1+50 ; bin: 8b 79 32
|
||||
[-,%rdi] v142 = uload32.i64 v1+50 ; bin: heap_oob 8b 79 32
|
||||
; asm: movl -50(%rsi), %edx
|
||||
[-,%rdx] v143 = uload32.i64 v2-50 ; bin: 8b 56 ce
|
||||
[-,%rdx] v143 = uload32.i64 v2-50 ; bin: heap_oob 8b 56 ce
|
||||
; asm: movslq 50(%rcx), %rdi
|
||||
[-,%rdi] v144 = sload32.i64 v1+50 ; bin: 48 63 79 32
|
||||
[-,%rdi] v144 = sload32.i64 v1+50 ; bin: heap_oob 48 63 79 32
|
||||
; asm: movslq -50(%rsi), %rdx
|
||||
[-,%rdx] v145 = sload32.i64 v2-50 ; bin: 48 63 56 ce
|
||||
[-,%rdx] v145 = sload32.i64 v2-50 ; bin: heap_oob 48 63 56 ce
|
||||
; asm: movzwq 50(%rcx), %rdi
|
||||
[-,%rdi] v146 = uload16.i64 v1+50 ; bin: 48 0f b7 79 32
|
||||
[-,%rdi] v146 = uload16.i64 v1+50 ; bin: heap_oob 48 0f b7 79 32
|
||||
; asm: movzwq -50(%rsi), %rdx
|
||||
[-,%rdx] v147 = uload16.i64 v2-50 ; bin: 48 0f b7 56 ce
|
||||
[-,%rdx] v147 = uload16.i64 v2-50 ; bin: heap_oob 48 0f b7 56 ce
|
||||
; asm: movswq 50(%rcx), %rdi
|
||||
[-,%rdi] v148 = sload16.i64 v1+50 ; bin: 48 0f bf 79 32
|
||||
[-,%rdi] v148 = sload16.i64 v1+50 ; bin: heap_oob 48 0f bf 79 32
|
||||
; asm: movswq -50(%rsi), %rdx
|
||||
[-,%rdx] v149 = sload16.i64 v2-50 ; bin: 48 0f bf 56 ce
|
||||
[-,%rdx] v149 = sload16.i64 v2-50 ; bin: heap_oob 48 0f bf 56 ce
|
||||
; asm: movzbq 50(%rcx), %rdi
|
||||
[-,%rdi] v150 = uload8.i64 v1+50 ; bin: 48 0f b6 79 32
|
||||
[-,%rdi] v150 = uload8.i64 v1+50 ; bin: heap_oob 48 0f b6 79 32
|
||||
; asm: movzbq -50(%rsi), %rdx
|
||||
[-,%rdx] v151 = uload8.i64 v2-50 ; bin: 48 0f b6 56 ce
|
||||
[-,%rdx] v151 = uload8.i64 v2-50 ; bin: heap_oob 48 0f b6 56 ce
|
||||
; asm: movsbq 50(%rcx), %rdi
|
||||
[-,%rdi] v152 = sload8.i64 v1+50 ; bin: 48 0f be 79 32
|
||||
[-,%rdi] v152 = sload8.i64 v1+50 ; bin: heap_oob 48 0f be 79 32
|
||||
; asm: movsbq -50(%rsi), %rdx
|
||||
[-,%rdx] v153 = sload8.i64 v2-50 ; bin: 48 0f be 56 ce
|
||||
[-,%rdx] v153 = sload8.i64 v2-50 ; bin: heap_oob 48 0f be 56 ce
|
||||
|
||||
; Register-indirect with 32-bit signed displacement.
|
||||
|
||||
; asm: movq %rcx, 10000(%r10)
|
||||
store v1, v3+10000 ; bin: 49 89 8a 00002710
|
||||
store v1, v3+10000 ; bin: heap_oob 49 89 8a 00002710
|
||||
; asm: movq %r10, -10000(%rcx)
|
||||
store v3, v1-10000 ; bin: 4c 89 91 ffffd8f0
|
||||
store v3, v1-10000 ; bin: heap_oob 4c 89 91 ffffd8f0
|
||||
; asm: movl %ecx, 10000(%rsi)
|
||||
istore32 v1, v2+10000 ; bin: 89 8e 00002710
|
||||
istore32 v1, v2+10000 ; bin: heap_oob 89 8e 00002710
|
||||
; asm: movl %esi, -10000(%rcx)
|
||||
istore32 v2, v1-10000 ; bin: 89 b1 ffffd8f0
|
||||
istore32 v2, v1-10000 ; bin: heap_oob 89 b1 ffffd8f0
|
||||
; asm: movw %cx, 10000(%rsi)
|
||||
istore16 v1, v2+10000 ; bin: 66 89 8e 00002710
|
||||
istore16 v1, v2+10000 ; bin: heap_oob 66 89 8e 00002710
|
||||
; asm: movw %si, -10000(%rcx)
|
||||
istore16 v2, v1-10000 ; bin: 66 89 b1 ffffd8f0
|
||||
istore16 v2, v1-10000 ; bin: heap_oob 66 89 b1 ffffd8f0
|
||||
; asm: movb %cl, 10000(%rsi)
|
||||
istore8 v1, v2+10000 ; bin: 88 8e 00002710
|
||||
istore8 v1, v2+10000 ; bin: heap_oob 88 8e 00002710
|
||||
; asm: movb %sil, 10000(%rcx)
|
||||
istore8 v2, v1+10000 ; bin: 40 88 b1 00002710
|
||||
istore8 v2, v1+10000 ; bin: heap_oob 40 88 b1 00002710
|
||||
|
||||
; asm: movq 50000(%rcx), %r10
|
||||
[-,%r10] v160 = load.i64 v1+50000 ; bin: 4c 8b 91 0000c350
|
||||
[-,%r10] v160 = load.i64 v1+50000 ; bin: heap_oob 4c 8b 91 0000c350
|
||||
; asm: movq -50000(%r10), %rdx
|
||||
[-,%rdx] v161 = load.i64 v3-50000 ; bin: 49 8b 92 ffff3cb0
|
||||
[-,%rdx] v161 = load.i64 v3-50000 ; bin: heap_oob 49 8b 92 ffff3cb0
|
||||
; asm: movl 50000(%rcx), %edi
|
||||
[-,%rdi] v162 = uload32.i64 v1+50000 ; bin: 8b b9 0000c350
|
||||
[-,%rdi] v162 = uload32.i64 v1+50000 ; bin: heap_oob 8b b9 0000c350
|
||||
; asm: movl -50000(%rsi), %edx
|
||||
[-,%rdx] v163 = uload32.i64 v2-50000 ; bin: 8b 96 ffff3cb0
|
||||
[-,%rdx] v163 = uload32.i64 v2-50000 ; bin: heap_oob 8b 96 ffff3cb0
|
||||
; asm: movslq 50000(%rcx), %rdi
|
||||
[-,%rdi] v164 = sload32.i64 v1+50000 ; bin: 48 63 b9 0000c350
|
||||
[-,%rdi] v164 = sload32.i64 v1+50000 ; bin: heap_oob 48 63 b9 0000c350
|
||||
; asm: movslq -50000(%rsi), %rdx
|
||||
[-,%rdx] v165 = sload32.i64 v2-50000 ; bin: 48 63 96 ffff3cb0
|
||||
[-,%rdx] v165 = sload32.i64 v2-50000 ; bin: heap_oob 48 63 96 ffff3cb0
|
||||
; asm: movzwq 50000(%rcx), %rdi
|
||||
[-,%rdi] v166 = uload16.i64 v1+50000 ; bin: 48 0f b7 b9 0000c350
|
||||
[-,%rdi] v166 = uload16.i64 v1+50000 ; bin: heap_oob 48 0f b7 b9 0000c350
|
||||
; asm: movzwq -50000(%rsi), %rdx
|
||||
[-,%rdx] v167 = uload16.i64 v2-50000 ; bin: 48 0f b7 96 ffff3cb0
|
||||
[-,%rdx] v167 = uload16.i64 v2-50000 ; bin: heap_oob 48 0f b7 96 ffff3cb0
|
||||
; asm: movswq 50000(%rcx), %rdi
|
||||
[-,%rdi] v168 = sload16.i64 v1+50000 ; bin: 48 0f bf b9 0000c350
|
||||
[-,%rdi] v168 = sload16.i64 v1+50000 ; bin: heap_oob 48 0f bf b9 0000c350
|
||||
; asm: movswq -50000(%rsi), %rdx
|
||||
[-,%rdx] v169 = sload16.i64 v2-50000 ; bin: 48 0f bf 96 ffff3cb0
|
||||
[-,%rdx] v169 = sload16.i64 v2-50000 ; bin: heap_oob 48 0f bf 96 ffff3cb0
|
||||
; asm: movzbq 50000(%rcx), %rdi
|
||||
[-,%rdi] v170 = uload8.i64 v1+50000 ; bin: 48 0f b6 b9 0000c350
|
||||
[-,%rdi] v170 = uload8.i64 v1+50000 ; bin: heap_oob 48 0f b6 b9 0000c350
|
||||
; asm: movzbq -50000(%rsi), %rdx
|
||||
[-,%rdx] v171 = uload8.i64 v2-50000 ; bin: 48 0f b6 96 ffff3cb0
|
||||
[-,%rdx] v171 = uload8.i64 v2-50000 ; bin: heap_oob 48 0f b6 96 ffff3cb0
|
||||
; asm: movsbq 50000(%rcx), %rdi
|
||||
[-,%rdi] v172 = sload8.i64 v1+50000 ; bin: 48 0f be b9 0000c350
|
||||
[-,%rdi] v172 = sload8.i64 v1+50000 ; bin: heap_oob 48 0f be b9 0000c350
|
||||
; asm: movsbq -50000(%rsi), %rdx
|
||||
[-,%rdx] v173 = sload8.i64 v2-50000 ; bin: 48 0f be 96 ffff3cb0
|
||||
[-,%rdx] v173 = sload8.i64 v2-50000 ; bin: heap_oob 48 0f be 96 ffff3cb0
|
||||
|
||||
|
||||
; More arithmetic.
|
||||
@@ -324,17 +329,17 @@ ebb0:
|
||||
[-,%rax] v190 = iconst.i64 1
|
||||
[-,%rdx] v191 = iconst.i64 2
|
||||
; asm: idivq %rcx
|
||||
[-,%rax,%rdx] v192, v193 = x86_sdivmodx v190, v191, v1 ; bin: 48 f7 f9
|
||||
[-,%rax,%rdx] v192, v193 = x86_sdivmodx v190, v191, v1 ; bin: int_divz 48 f7 f9
|
||||
; asm: idivq %rsi
|
||||
[-,%rax,%rdx] v194, v195 = x86_sdivmodx v190, v191, v2 ; bin: 48 f7 fe
|
||||
[-,%rax,%rdx] v194, v195 = x86_sdivmodx v190, v191, v2 ; bin: int_divz 48 f7 fe
|
||||
; asm: idivq %r10
|
||||
[-,%rax,%rdx] v196, v197 = x86_sdivmodx v190, v191, v3 ; bin: 49 f7 fa
|
||||
[-,%rax,%rdx] v196, v197 = x86_sdivmodx v190, v191, v3 ; bin: int_divz 49 f7 fa
|
||||
; asm: divq %rcx
|
||||
[-,%rax,%rdx] v198, v199 = x86_udivmodx v190, v191, v1 ; bin: 48 f7 f1
|
||||
[-,%rax,%rdx] v198, v199 = x86_udivmodx v190, v191, v1 ; bin: int_divz 48 f7 f1
|
||||
; asm: divq %rsi
|
||||
[-,%rax,%rdx] v200, v201 = x86_udivmodx v190, v191, v2 ; bin: 48 f7 f6
|
||||
[-,%rax,%rdx] v200, v201 = x86_udivmodx v190, v191, v2 ; bin: int_divz 48 f7 f6
|
||||
; asm: divq %r10
|
||||
[-,%rax,%rdx] v202, v203 = x86_udivmodx v190, v191, v3 ; bin: 49 f7 f2
|
||||
[-,%rax,%rdx] v202, v203 = x86_udivmodx v190, v191, v3 ; bin: int_divz 49 f7 f2
|
||||
|
||||
; double-length multiply instructions, 64 bit
|
||||
[-,%rax] v1001 = iconst.i64 1
|
||||
@@ -453,6 +458,14 @@ ebb0:
|
||||
; asm: setbe %dl
|
||||
[-,%rdx] v319 = icmp ule v2, v3 ; bin: 4c 39 d6 0f 96 c2
|
||||
|
||||
; asm: cmpq $37, %rcx
|
||||
; asm: setl %bl
|
||||
[-,%rbx] v320 = icmp_imm slt v1, 37 ; bin: 48 83 f9 25 0f 9c c3
|
||||
|
||||
; asm: cmpq $100000, %rcx
|
||||
; asm: setl %bl
|
||||
[-,%rbx] v321 = icmp_imm slt v1, 100000 ; bin: 48 81 f9 000186a0 0f 9c c3
|
||||
|
||||
; Bool-to-int conversions.
|
||||
|
||||
; asm: movzbq %bl, %rcx
|
||||
@@ -529,6 +542,21 @@ ebb0:
|
||||
; asm: addq $-2147483648, %rsp
|
||||
adjust_sp_imm -2147483648 ; bin: 48 81 c4 80000000
|
||||
|
||||
; Shift immediates
|
||||
; asm: shlq $12, %rsi
|
||||
[-,%rsi] v515 = ishl_imm v2, 12 ; bin: 48 c1 e6 0c
|
||||
; asm: shlq $13, %r8
|
||||
[-,%r8] v516 = ishl_imm v4, 13 ; bin: 49 c1 e0 0d
|
||||
; asm: sarq $32, %rsi
|
||||
[-,%rsi] v517 = sshr_imm v2, 32 ; bin: 48 c1 fe 20
|
||||
; asm: sarq $33, %r8
|
||||
[-,%r8] v518 = sshr_imm v4, 33 ; bin: 49 c1 f8 21
|
||||
; asm: shrl $62, %rsi
|
||||
[-,%rsi] v519 = ushr_imm v2, 62 ; bin: 48 c1 ee 3e
|
||||
; asm: shrl $63, %r8
|
||||
[-,%r8] v520 = ushr_imm v4, 63 ; bin: 49 c1 e8 3f
|
||||
|
||||
|
||||
; asm: testq %rcx, %rcx
|
||||
; asm: je ebb1
|
||||
brz v1, ebb1 ; bin: 48 85 c9 74 1b
|
||||
@@ -569,9 +597,9 @@ ebb0:
|
||||
|
||||
ebb1:
|
||||
; asm: cmpq %r10, %rcx
|
||||
[-,%eflags] v10 = ifcmp v1, v2 ; bin: 4c 39 d1
|
||||
[-,%rflags] v10 = ifcmp v1, v2 ; bin: 4c 39 d1
|
||||
; asm: cmpq %rcx, %r10
|
||||
[-,%eflags] v11 = ifcmp v2, v1 ; bin: 49 39 ca
|
||||
[-,%rflags] v11 = ifcmp v2, v1 ; bin: 49 39 ca
|
||||
|
||||
; asm: je ebb1
|
||||
brif eq v11, ebb1 ; bin: 74 f8
|
||||
@@ -617,41 +645,42 @@ ebb1:
|
||||
|
||||
; The trapif instructions are encoded as macros: a conditional jump over a ud2.
|
||||
; asm: jne .+4; ud2
|
||||
trapif eq v11, user0 ; bin: 75 02 0f 0b
|
||||
trapif eq v11, user0 ; bin: 75 02 user0 0f 0b
|
||||
; asm: je .+4; ud2
|
||||
trapif ne v11, user0 ; bin: 74 02 0f 0b
|
||||
trapif ne v11, user0 ; bin: 74 02 user0 0f 0b
|
||||
; asm: jnl .+4; ud2
|
||||
trapif slt v11, user0 ; bin: 7d 02 0f 0b
|
||||
trapif slt v11, user0 ; bin: 7d 02 user0 0f 0b
|
||||
; asm: jnge .+4; ud2
|
||||
trapif sge v11, user0 ; bin: 7c 02 0f 0b
|
||||
trapif sge v11, user0 ; bin: 7c 02 user0 0f 0b
|
||||
; asm: jng .+4; ud2
|
||||
trapif sgt v11, user0 ; bin: 7e 02 0f 0b
|
||||
trapif sgt v11, user0 ; bin: 7e 02 user0 0f 0b
|
||||
; asm: jnle .+4; ud2
|
||||
trapif sle v11, user0 ; bin: 7f 02 0f 0b
|
||||
trapif sle v11, user0 ; bin: 7f 02 user0 0f 0b
|
||||
; asm: jnb .+4; ud2
|
||||
trapif ult v11, user0 ; bin: 73 02 0f 0b
|
||||
trapif ult v11, user0 ; bin: 73 02 user0 0f 0b
|
||||
; asm: jnae .+4; ud2
|
||||
trapif uge v11, user0 ; bin: 72 02 0f 0b
|
||||
trapif uge v11, user0 ; bin: 72 02 user0 0f 0b
|
||||
; asm: jna .+4; ud2
|
||||
trapif ugt v11, user0 ; bin: 76 02 0f 0b
|
||||
trapif ugt v11, user0 ; bin: 76 02 user0 0f 0b
|
||||
; asm: jnbe .+4; ud2
|
||||
trapif ule v11, user0 ; bin: 77 02 0f 0b
|
||||
trapif ule v11, user0 ; bin: 77 02 user0 0f 0b
|
||||
|
||||
; Stack check.
|
||||
; asm: cmpq %rsp, %rcx
|
||||
[-,%eflags] v40 = ifcmp_sp v1 ; bin: 48 39 e1
|
||||
[-,%rflags] v40 = ifcmp_sp v1 ; bin: 48 39 e1
|
||||
; asm: cmpq %rsp, %r10
|
||||
[-,%eflags] v41 = ifcmp_sp v2 ; bin: 49 39 e2
|
||||
[-,%rflags] v41 = ifcmp_sp v2 ; bin: 49 39 e2
|
||||
|
||||
; asm: cmpq $-100, %rcx
|
||||
[-,%eflags] v522 = ifcmp_imm v1, -100 ; bin: 48 83 f9 9c
|
||||
[-,%rflags] v522 = ifcmp_imm v1, -100 ; bin: 48 83 f9 9c
|
||||
; asm: cmpq $100, %r10
|
||||
[-,%eflags] v523 = ifcmp_imm v2, 100 ; bin: 49 83 fa 64
|
||||
[-,%rflags] v523 = ifcmp_imm v2, 100 ; bin: 49 83 fa 64
|
||||
|
||||
; asm: cmpq $-10000, %rcx
|
||||
[-,%eflags] v524 = ifcmp_imm v1, -10000 ; bin: 48 81 f9 ffffd8f0
|
||||
[-,%rflags] v524 = ifcmp_imm v1, -10000 ; bin: 48 81 f9 ffffd8f0
|
||||
; asm: cmpq $10000, %r10
|
||||
[-,%eflags] v525 = ifcmp_imm v2, 10000 ; bin: 49 81 fa 00002710
|
||||
[-,%rflags] v525 = ifcmp_imm v2, 10000 ; bin: 49 81 fa 00002710
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
@@ -708,71 +737,71 @@ ebb0:
|
||||
; Register indirect addressing with no displacement.
|
||||
|
||||
; asm: movl (%rcx), %edi
|
||||
[-,%rdi] v10 = load.i32 v1 ; bin: 8b 39
|
||||
[-,%rdi] v10 = load.i32 v1 ; bin: heap_oob 8b 39
|
||||
; asm: movl (%rsi), %edx
|
||||
[-,%rdx] v11 = load.i32 v2 ; bin: 8b 16
|
||||
[-,%rdx] v11 = load.i32 v2 ; bin: heap_oob 8b 16
|
||||
; asm: movzwl (%rcx), %edi
|
||||
[-,%rdi] v12 = uload16.i32 v1 ; bin: 0f b7 39
|
||||
[-,%rdi] v12 = uload16.i32 v1 ; bin: heap_oob 0f b7 39
|
||||
; asm: movzwl (%rsi), %edx
|
||||
[-,%rdx] v13 = uload16.i32 v2 ; bin: 0f b7 16
|
||||
[-,%rdx] v13 = uload16.i32 v2 ; bin: heap_oob 0f b7 16
|
||||
; asm: movswl (%rcx), %edi
|
||||
[-,%rdi] v14 = sload16.i32 v1 ; bin: 0f bf 39
|
||||
[-,%rdi] v14 = sload16.i32 v1 ; bin: heap_oob 0f bf 39
|
||||
; asm: movswl (%rsi), %edx
|
||||
[-,%rdx] v15 = sload16.i32 v2 ; bin: 0f bf 16
|
||||
[-,%rdx] v15 = sload16.i32 v2 ; bin: heap_oob 0f bf 16
|
||||
; asm: movzbl (%rcx), %edi
|
||||
[-,%rdi] v16 = uload8.i32 v1 ; bin: 0f b6 39
|
||||
[-,%rdi] v16 = uload8.i32 v1 ; bin: heap_oob 0f b6 39
|
||||
; asm: movzbl (%rsi), %edx
|
||||
[-,%rdx] v17 = uload8.i32 v2 ; bin: 0f b6 16
|
||||
[-,%rdx] v17 = uload8.i32 v2 ; bin: heap_oob 0f b6 16
|
||||
; asm: movsbl (%rcx), %edi
|
||||
[-,%rdi] v18 = sload8.i32 v1 ; bin: 0f be 39
|
||||
[-,%rdi] v18 = sload8.i32 v1 ; bin: heap_oob 0f be 39
|
||||
; asm: movsbl (%rsi), %edx
|
||||
[-,%rdx] v19 = sload8.i32 v2 ; bin: 0f be 16
|
||||
[-,%rdx] v19 = sload8.i32 v2 ; bin: heap_oob 0f be 16
|
||||
|
||||
; Register-indirect with 8-bit signed displacement.
|
||||
|
||||
; asm: movl 50(%rcx), %edi
|
||||
[-,%rdi] v20 = load.i32 v1+50 ; bin: 8b 79 32
|
||||
[-,%rdi] v20 = load.i32 v1+50 ; bin: heap_oob 8b 79 32
|
||||
; asm: movl -50(%rsi), %edx
|
||||
[-,%rdx] v21 = load.i32 v2-50 ; bin: 8b 56 ce
|
||||
[-,%rdx] v21 = load.i32 v2-50 ; bin: heap_oob 8b 56 ce
|
||||
; asm: movzwl 50(%rcx), %edi
|
||||
[-,%rdi] v22 = uload16.i32 v1+50 ; bin: 0f b7 79 32
|
||||
[-,%rdi] v22 = uload16.i32 v1+50 ; bin: heap_oob 0f b7 79 32
|
||||
; asm: movzwl -50(%rsi), %edx
|
||||
[-,%rdx] v23 = uload16.i32 v2-50 ; bin: 0f b7 56 ce
|
||||
[-,%rdx] v23 = uload16.i32 v2-50 ; bin: heap_oob 0f b7 56 ce
|
||||
; asm: movswl 50(%rcx), %edi
|
||||
[-,%rdi] v24 = sload16.i32 v1+50 ; bin: 0f bf 79 32
|
||||
[-,%rdi] v24 = sload16.i32 v1+50 ; bin: heap_oob 0f bf 79 32
|
||||
; asm: movswl -50(%rsi), %edx
|
||||
[-,%rdx] v25 = sload16.i32 v2-50 ; bin: 0f bf 56 ce
|
||||
[-,%rdx] v25 = sload16.i32 v2-50 ; bin: heap_oob 0f bf 56 ce
|
||||
; asm: movzbl 50(%rcx), %edi
|
||||
[-,%rdi] v26 = uload8.i32 v1+50 ; bin: 0f b6 79 32
|
||||
[-,%rdi] v26 = uload8.i32 v1+50 ; bin: heap_oob 0f b6 79 32
|
||||
; asm: movzbl -50(%rsi), %edx
|
||||
[-,%rdx] v27 = uload8.i32 v2-50 ; bin: 0f b6 56 ce
|
||||
[-,%rdx] v27 = uload8.i32 v2-50 ; bin: heap_oob 0f b6 56 ce
|
||||
; asm: movsbl 50(%rcx), %edi
|
||||
[-,%rdi] v28 = sload8.i32 v1+50 ; bin: 0f be 79 32
|
||||
[-,%rdi] v28 = sload8.i32 v1+50 ; bin: heap_oob 0f be 79 32
|
||||
; asm: movsbl -50(%rsi), %edx
|
||||
[-,%rdx] v29 = sload8.i32 v2-50 ; bin: 0f be 56 ce
|
||||
[-,%rdx] v29 = sload8.i32 v2-50 ; bin: heap_oob 0f be 56 ce
|
||||
|
||||
; Register-indirect with 32-bit signed displacement.
|
||||
|
||||
; asm: movl 50000(%rcx), %edi
|
||||
[-,%rdi] v30 = load.i32 v1+50000 ; bin: 8b b9 0000c350
|
||||
[-,%rdi] v30 = load.i32 v1+50000 ; bin: heap_oob 8b b9 0000c350
|
||||
; asm: movl -50000(%rsi), %edx
|
||||
[-,%rdx] v31 = load.i32 v2-50000 ; bin: 8b 96 ffff3cb0
|
||||
[-,%rdx] v31 = load.i32 v2-50000 ; bin: heap_oob 8b 96 ffff3cb0
|
||||
; asm: movzwl 50000(%rcx), %edi
|
||||
[-,%rdi] v32 = uload16.i32 v1+50000 ; bin: 0f b7 b9 0000c350
|
||||
[-,%rdi] v32 = uload16.i32 v1+50000 ; bin: heap_oob 0f b7 b9 0000c350
|
||||
; asm: movzwl -50000(%rsi), %edx
|
||||
[-,%rdx] v33 = uload16.i32 v2-50000 ; bin: 0f b7 96 ffff3cb0
|
||||
[-,%rdx] v33 = uload16.i32 v2-50000 ; bin: heap_oob 0f b7 96 ffff3cb0
|
||||
; asm: movswl 50000(%rcx), %edi
|
||||
[-,%rdi] v34 = sload16.i32 v1+50000 ; bin: 0f bf b9 0000c350
|
||||
[-,%rdi] v34 = sload16.i32 v1+50000 ; bin: heap_oob 0f bf b9 0000c350
|
||||
; asm: movswl -50000(%rsi), %edx
|
||||
[-,%rdx] v35 = sload16.i32 v2-50000 ; bin: 0f bf 96 ffff3cb0
|
||||
[-,%rdx] v35 = sload16.i32 v2-50000 ; bin: heap_oob 0f bf 96 ffff3cb0
|
||||
; asm: movzbl 50000(%rcx), %edi
|
||||
[-,%rdi] v36 = uload8.i32 v1+50000 ; bin: 0f b6 b9 0000c350
|
||||
[-,%rdi] v36 = uload8.i32 v1+50000 ; bin: heap_oob 0f b6 b9 0000c350
|
||||
; asm: movzbl -50000(%rsi), %edx
|
||||
[-,%rdx] v37 = uload8.i32 v2-50000 ; bin: 0f b6 96 ffff3cb0
|
||||
[-,%rdx] v37 = uload8.i32 v2-50000 ; bin: heap_oob 0f b6 96 ffff3cb0
|
||||
; asm: movsbl 50000(%rcx), %edi
|
||||
[-,%rdi] v38 = sload8.i32 v1+50000 ; bin: 0f be b9 0000c350
|
||||
[-,%rdi] v38 = sload8.i32 v1+50000 ; bin: heap_oob 0f be b9 0000c350
|
||||
; asm: movsbl -50000(%rsi), %edx
|
||||
[-,%rdx] v39 = sload8.i32 v2-50000 ; bin: 0f be 96 ffff3cb0
|
||||
[-,%rdx] v39 = sload8.i32 v2-50000 ; bin: heap_oob 0f be 96 ffff3cb0
|
||||
|
||||
; Integer Register-Register Operations.
|
||||
|
||||
@@ -903,17 +932,17 @@ ebb0:
|
||||
[-,%rax] v160 = iconst.i32 1
|
||||
[-,%rdx] v161 = iconst.i32 2
|
||||
; asm: idivl %ecx
|
||||
[-,%rax,%rdx] v162, v163 = x86_sdivmodx v160, v161, v1 ; bin: f7 f9
|
||||
[-,%rax,%rdx] v162, v163 = x86_sdivmodx v160, v161, v1 ; bin: int_divz f7 f9
|
||||
; asm: idivl %esi
|
||||
[-,%rax,%rdx] v164, v165 = x86_sdivmodx v160, v161, v2 ; bin: f7 fe
|
||||
[-,%rax,%rdx] v164, v165 = x86_sdivmodx v160, v161, v2 ; bin: int_divz f7 fe
|
||||
; asm: idivl %r10d
|
||||
[-,%rax,%rdx] v166, v167 = x86_sdivmodx v160, v161, v3 ; bin: 41 f7 fa
|
||||
[-,%rax,%rdx] v166, v167 = x86_sdivmodx v160, v161, v3 ; bin: int_divz 41 f7 fa
|
||||
; asm: divl %ecx
|
||||
[-,%rax,%rdx] v168, v169 = x86_udivmodx v160, v161, v1 ; bin: f7 f1
|
||||
[-,%rax,%rdx] v168, v169 = x86_udivmodx v160, v161, v1 ; bin: int_divz f7 f1
|
||||
; asm: divl %esi
|
||||
[-,%rax,%rdx] v170, v171 = x86_udivmodx v160, v161, v2 ; bin: f7 f6
|
||||
[-,%rax,%rdx] v170, v171 = x86_udivmodx v160, v161, v2 ; bin: int_divz f7 f6
|
||||
; asm: divl %r10d
|
||||
[-,%rax,%rdx] v172, v173 = x86_udivmodx v160, v161, v3 ; bin: 41 f7 f2
|
||||
[-,%rax,%rdx] v172, v173 = x86_udivmodx v160, v161, v3 ; bin: int_divz 41 f7 f2
|
||||
|
||||
; Bit-counting instructions.
|
||||
|
||||
@@ -1010,6 +1039,14 @@ ebb0:
|
||||
; asm: setbe %dl
|
||||
[-,%rdx] v319 = icmp ule v2, v3 ; bin: 44 39 d6 0f 96 c2
|
||||
|
||||
; asm: cmpl $37, %ecx
|
||||
; asm: setl %bl
|
||||
[-,%rbx] v320 = icmp_imm slt v1, 37 ; bin: 83 f9 25 0f 9c c3
|
||||
|
||||
; asm: cmpq $100000, %ecx
|
||||
; asm: setl %bl
|
||||
[-,%rbx] v321 = icmp_imm slt v1, 100000 ; bin: 81 f9 000186a0 0f 9c c3
|
||||
|
||||
; Bool-to-int conversions.
|
||||
|
||||
; asm: movzbl %bl, %ecx
|
||||
@@ -1039,19 +1076,32 @@ ebb0:
|
||||
regfill v1, ss1 -> %rcx ; bin: 8b 8c 24 00000408
|
||||
|
||||
; asm: cmpl %esi, %ecx
|
||||
[-,%eflags] v520 = ifcmp v1, v2 ; bin: 39 f1
|
||||
[-,%rflags] v520 = ifcmp v1, v2 ; bin: 39 f1
|
||||
; asm: cmpl %r10d, %esi
|
||||
[-,%eflags] v521 = ifcmp v2, v3 ; bin: 44 39 d6
|
||||
[-,%rflags] v521 = ifcmp v2, v3 ; bin: 44 39 d6
|
||||
|
||||
; asm: cmpl $-100, %ecx
|
||||
[-,%eflags] v522 = ifcmp_imm v1, -100 ; bin: 83 f9 9c
|
||||
[-,%rflags] v522 = ifcmp_imm v1, -100 ; bin: 83 f9 9c
|
||||
; asm: cmpl $100, %r10d
|
||||
[-,%eflags] v523 = ifcmp_imm v3, 100 ; bin: 41 83 fa 64
|
||||
[-,%rflags] v523 = ifcmp_imm v3, 100 ; bin: 41 83 fa 64
|
||||
|
||||
; asm: cmpl $-10000, %ecx
|
||||
[-,%eflags] v524 = ifcmp_imm v1, -10000 ; bin: 81 f9 ffffd8f0
|
||||
[-,%rflags] v524 = ifcmp_imm v1, -10000 ; bin: 81 f9 ffffd8f0
|
||||
; asm: cmpl $10000, %r10d
|
||||
[-,%eflags] v525 = ifcmp_imm v3, 10000 ; bin: 41 81 fa 00002710
|
||||
[-,%rflags] v525 = ifcmp_imm v3, 10000 ; bin: 41 81 fa 00002710
|
||||
|
||||
; asm: shll $2, %esi
|
||||
[-,%rsi] v526 = ishl_imm v2, 2 ; bin: c1 e6 02
|
||||
; asm: shll $12, %r10d
|
||||
[-,%r10] v527 = ishl_imm v3, 12 ; bin: 41 c1 e2 0c
|
||||
; asm: sarl $5, %esi
|
||||
[-,%rsi] v529 = sshr_imm v2, 5 ; bin: c1 fe 05
|
||||
; asm: sarl $32, %r10d
|
||||
[-,%r10] v530 = sshr_imm v3, 32 ; bin: 41 c1 fa 20
|
||||
; asm: shrl $8, %esi
|
||||
[-,%rsi] v532 = ushr_imm v2, 8 ; bin: c1 ee 08
|
||||
; asm: shrl $31, %r10d
|
||||
[-,%r10] v533 = ushr_imm v3, 31 ; bin: 41 c1 ea 1f
|
||||
|
||||
; asm: testl %ecx, %ecx
|
||||
; asm: je ebb1x
|
||||
@@ -1082,6 +1132,7 @@ ebb1:
|
||||
; asm: ebb2x:
|
||||
ebb2:
|
||||
jump ebb1 ; bin: eb fd
|
||||
|
||||
}
|
||||
|
||||
; Tests for i32/i8 conversion instructions.
|
||||
@@ -1109,7 +1160,7 @@ ebb0:
|
||||
; asm: movzbl %r10b, %ecx
|
||||
[-,%rcx] v32 = uextend.i32 v13 ; bin: 41 0f b6 ca
|
||||
|
||||
trap user0 ; bin: 0f 0b
|
||||
trap user0 ; bin: user0 0f 0b
|
||||
}
|
||||
|
||||
; Tests for i32/i16 conversion instructions.
|
||||
@@ -1137,7 +1188,7 @@ ebb0:
|
||||
; asm: movzwl %r10w, %ecx
|
||||
[-,%rcx] v32 = uextend.i32 v13 ; bin: 41 0f b7 ca
|
||||
|
||||
trap user0 ; bin: 0f 0b
|
||||
trap user0 ; bin: user0 0f 0b
|
||||
}
|
||||
|
||||
; Tests for i64/i8 conversion instructions.
|
||||
@@ -1165,7 +1216,7 @@ ebb0:
|
||||
; asm: movzbl %r10b, %ecx
|
||||
[-,%rcx] v32 = uextend.i64 v13 ; bin: 41 0f b6 ca
|
||||
|
||||
trap user0 ; bin: 0f 0b
|
||||
trap user0 ; bin: user0 0f 0b
|
||||
}
|
||||
|
||||
; Tests for i64/i16 conversion instructions.
|
||||
@@ -1193,7 +1244,7 @@ ebb0:
|
||||
; asm: movzwl %r10w, %ecx
|
||||
[-,%rcx] v32 = uextend.i64 v13 ; bin: 41 0f b7 ca
|
||||
|
||||
trap user0 ; bin: 0f 0b
|
||||
trap user0 ; bin: user0 0f 0b
|
||||
}
|
||||
|
||||
; Tests for i64/i32 conversion instructions.
|
||||
@@ -1221,5 +1272,5 @@ ebb0:
|
||||
; asm: movl %r10d, %ecx
|
||||
[-,%rcx] v32 = uextend.i64 v13 ; bin: 44 89 d1
|
||||
|
||||
trap user0 ; bin: 0f 0b
|
||||
trap user0 ; bin: user0 0f 0b
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ ebb0(v0: i64, v1: i64):
|
||||
; nextln: brif eq $fm1, $(m1=$EBB)
|
||||
; nextln: $(fz=$V) = ifcmp_imm v1, 0
|
||||
; nextln: trapif eq $fz, int_divz
|
||||
; check: $(hi=$V) = sshr
|
||||
; check: $(hi=$V) = sshr_imm
|
||||
; nextln: $(q=$V), $(r=$V) = x86_sdivmodx v0, $hi, v1
|
||||
; nextln: jump $(done=$EBB)($q)
|
||||
; check: $m1:
|
||||
@@ -60,7 +60,7 @@ ebb0(v0: i64, v1: i64):
|
||||
v2 = srem v0, v1
|
||||
; nextln: $(fm1=$V) = ifcmp_imm v1, -1
|
||||
; nextln: brif eq $fm1, $(m1=$EBB)
|
||||
; check: $(hi=$V) = sshr
|
||||
; check: $(hi=$V) = sshr_imm
|
||||
; nextln: $(d=$V), $(r=$V) = x86_sdivmodx v0, $hi, v1
|
||||
; nextln: jump $(done=$EBB)($r)
|
||||
; check: $m1:
|
||||
|
||||
@@ -32,7 +32,7 @@ function %sdiv(i64, i64) -> i64 {
|
||||
ebb0(v0: i64, v1: i64):
|
||||
; check: ebb0(
|
||||
v2 = sdiv v0, v1
|
||||
; check: $(hi=$V) = sshr
|
||||
; check: $(hi=$V) = sshr_imm
|
||||
; nextln: $(d=$V), $(r=$V) = x86_sdivmodx v0, $hi, v1
|
||||
return v2
|
||||
; nextln: return $d
|
||||
@@ -46,7 +46,7 @@ ebb0(v0: i64, v1: i64):
|
||||
v2 = srem v0, v1
|
||||
; nextln: $(fm1=$V) = ifcmp_imm v1, -1
|
||||
; nextln: brif eq $fm1, $(m1=$EBB)
|
||||
; check: $(hi=$V) = sshr
|
||||
; check: $(hi=$V) = sshr_imm
|
||||
; nextln: $(d=$V), $(r=$V) = x86_sdivmodx v0, $hi, v1
|
||||
; nextln: jump $(done=$EBB)($r)
|
||||
; check: $m1:
|
||||
|
||||
@@ -9,7 +9,7 @@ ebb0(v0: f32):
|
||||
v1 = floor v0
|
||||
return v1
|
||||
}
|
||||
; check: function %floor(f32 [%xmm0]) -> f32 [%xmm0] native {
|
||||
; check: sig0 = (f32) -> f32 native
|
||||
; check: function %floor(f32 [%xmm0]) -> f32 [%xmm0] system_v {
|
||||
; check: sig0 = (f32) -> f32 system_v
|
||||
; check: fn0 = sig0 %FloorF32
|
||||
; check: v1 = call fn0(v0)
|
||||
|
||||
@@ -23,7 +23,7 @@ function %deref(i64 vmctx) -> i64 {
|
||||
ebb1(v1: i64):
|
||||
v2 = global_addr.i64 gv2
|
||||
; check: $(a1=$V) = iadd_imm v1, -16
|
||||
; check: $(p1=$V) = load.i64 $a1
|
||||
; check: $(p1=$V) = load.i64 notrap aligned $a1
|
||||
; check: v2 = iadd_imm $p1, 32
|
||||
return v2
|
||||
; check: return v2
|
||||
@@ -55,7 +55,7 @@ ebb0(v0: i32, v999: i64):
|
||||
; Checks here are assuming that no pipehole opts fold the load offsets.
|
||||
; nextln: $(xoff=$V) = uextend.i64 v0
|
||||
; nextln: $(haddr=$V) = iadd_imm v999, 64
|
||||
; nextln: $(hbase=$V) = load.i64 $haddr
|
||||
; nextln: $(hbase=$V) = load.i64 notrap aligned $haddr
|
||||
; nextln: v1 = iadd $hbase, $xoff
|
||||
v2 = load.f32 v1+16
|
||||
; nextln: v2 = load.f32 v1+16
|
||||
@@ -103,7 +103,7 @@ ebb0(v0: i32, v999: i64):
|
||||
; Checks here are assuming that no pipehole opts fold the load offsets.
|
||||
; nextln: $(xoff=$V) = uextend.i64 v0
|
||||
; nextln: $(haddr=$V) = iadd_imm.i64 v999, 64
|
||||
; nextln: $(hbase=$V) = load.i64 $haddr
|
||||
; nextln: $(hbase=$V) = load.i64 notrap aligned $haddr
|
||||
; nextln: v1 = iadd $hbase, $xoff
|
||||
v2 = load.f32 v1+0x7fff_ffff
|
||||
; nextln: v2 = load.f32 v1+0x7fff_ffff
|
||||
|
||||
@@ -9,7 +9,7 @@ ebb0:
|
||||
return
|
||||
}
|
||||
|
||||
; check: function %foo(i64 fp [%rbp], i64 csr [%rbx], i64 csr [%r12], i64 csr [%r13], i64 csr [%r14], i64 csr [%r15]) -> i64 fp [%rbp], i64 csr [%rbx], i64 csr [%r12], i64 csr [%r13], i64 csr [%r14], i64 csr [%r15] native {
|
||||
; check: function %foo(i64 fp [%rbp], i64 csr [%rbx], i64 csr [%r12], i64 csr [%r13], i64 csr [%r14], i64 csr [%r15]) -> i64 fp [%rbp], i64 csr [%rbx], i64 csr [%r12], i64 csr [%r13], i64 csr [%r14], i64 csr [%r15] system_v {
|
||||
; nextln: ss0 = explicit_slot 168, offset -224
|
||||
; nextln: ss1 = incoming_arg 56, offset -56
|
||||
; check: ebb0(v0: i64 [%rbp], v1: i64 [%rbx], v2: i64 [%r12], v3: i64 [%r13], v4: i64 [%r14], v5: i64 [%r15]):
|
||||
|
||||
@@ -7,8 +7,8 @@ isa riscv enable_e
|
||||
function %f() {
|
||||
; Spilling into the stack args after %x15 since %16 and up are not
|
||||
; available in RV32E.
|
||||
sig0 = (i64, i64, i64, i64) -> i64 native
|
||||
; check: sig0 = (i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13], i32 [%x14], i32 [%x15], i32 [0], i32 [4]) -> i32 [%x10], i32 [%x11] native
|
||||
sig0 = (i64, i64, i64, i64) -> i64 system_v
|
||||
; check: sig0 = (i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13], i32 [%x14], i32 [%x15], i32 [0], i32 [4]) -> i32 [%x10], i32 [%x11] system_v
|
||||
ebb0:
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,27 +5,27 @@ isa riscv
|
||||
; regex: V=v\d+
|
||||
|
||||
function %f() {
|
||||
sig0 = (i32) -> i32 native
|
||||
; check: sig0 = (i32 [%x10]) -> i32 [%x10] native
|
||||
sig0 = (i32) -> i32 system_v
|
||||
; check: sig0 = (i32 [%x10]) -> i32 [%x10] system_v
|
||||
|
||||
sig1 = (i64) -> b1 native
|
||||
; check: sig1 = (i32 [%x10], i32 [%x11]) -> b1 [%x10] native
|
||||
sig1 = (i64) -> b1 system_v
|
||||
; check: sig1 = (i32 [%x10], i32 [%x11]) -> b1 [%x10] system_v
|
||||
|
||||
; The i64 argument must go in an even-odd register pair.
|
||||
sig2 = (f32, i64) -> f64 native
|
||||
; check: sig2 = (f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] native
|
||||
sig2 = (f32, i64) -> f64 system_v
|
||||
; check: sig2 = (f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] system_v
|
||||
|
||||
; Spilling into the stack args.
|
||||
sig3 = (f64, f64, f64, f64, f64, f64, f64, i64) -> f64 native
|
||||
; check: sig3 = (f64 [%f10], f64 [%f11], f64 [%f12], f64 [%f13], f64 [%f14], f64 [%f15], f64 [%f16], i32 [0], i32 [4]) -> f64 [%f10] native
|
||||
sig3 = (f64, f64, f64, f64, f64, f64, f64, i64) -> f64 system_v
|
||||
; check: sig3 = (f64 [%f10], f64 [%f11], f64 [%f12], f64 [%f13], f64 [%f14], f64 [%f15], f64 [%f16], i32 [0], i32 [4]) -> f64 [%f10] system_v
|
||||
|
||||
; Splitting vectors.
|
||||
sig4 = (i32x4) native
|
||||
; check: sig4 = (i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13]) native
|
||||
sig4 = (i32x4) system_v
|
||||
; check: sig4 = (i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13]) system_v
|
||||
|
||||
; Splitting vectors, then splitting ints.
|
||||
sig5 = (i64x4) native
|
||||
; check: sig5 = (i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13], i32 [%x14], i32 [%x15], i32 [%x16], i32 [%x17]) native
|
||||
sig5 = (i64x4) system_v
|
||||
; check: sig5 = (i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13], i32 [%x14], i32 [%x15], i32 [%x16], i32 [%x17]) system_v
|
||||
|
||||
ebb0:
|
||||
return
|
||||
|
||||
@@ -106,7 +106,7 @@ ebb0(v0: i64x4):
|
||||
}
|
||||
|
||||
function %indirect(i32) {
|
||||
sig1 = () native
|
||||
sig1 = () system_v
|
||||
ebb0(v0: i32):
|
||||
call_indirect sig1, v0()
|
||||
return
|
||||
@@ -114,7 +114,7 @@ ebb0(v0: i32):
|
||||
|
||||
; The first argument to call_indirect doesn't get altered.
|
||||
function %indirect_arg(i32, f32x2) {
|
||||
sig1 = (f32x2) native
|
||||
sig1 = (f32x2) system_v
|
||||
ebb0(v0: i32, v1: f32x2):
|
||||
call_indirect sig1, v0(v1)
|
||||
; check: call_indirect sig1, v0($V, $V)
|
||||
|
||||
@@ -3,32 +3,32 @@ test legalizer
|
||||
isa riscv
|
||||
|
||||
function %parse_encoding(i32 [%x5]) -> i32 [%x10] {
|
||||
; check: function %parse_encoding(i32 [%x5], i32 link [%x1]) -> i32 [%x10], i32 link [%x1] native {
|
||||
; check: function %parse_encoding(i32 [%x5], i32 link [%x1]) -> i32 [%x10], i32 link [%x1] system_v {
|
||||
|
||||
sig0 = (i32 [%x10]) -> i32 [%x10] native
|
||||
; check: sig0 = (i32 [%x10]) -> i32 [%x10] native
|
||||
sig0 = (i32 [%x10]) -> i32 [%x10] system_v
|
||||
; check: sig0 = (i32 [%x10]) -> i32 [%x10] system_v
|
||||
|
||||
sig1 = (i32 [%x10], i32 [%x11]) -> b1 [%x10] native
|
||||
; check: sig1 = (i32 [%x10], i32 [%x11]) -> b1 [%x10] native
|
||||
sig1 = (i32 [%x10], i32 [%x11]) -> b1 [%x10] system_v
|
||||
; check: sig1 = (i32 [%x10], i32 [%x11]) -> b1 [%x10] system_v
|
||||
|
||||
sig2 = (f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] native
|
||||
; check: sig2 = (f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] native
|
||||
sig2 = (f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] system_v
|
||||
; check: sig2 = (f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] system_v
|
||||
|
||||
; Arguments on stack where not necessary
|
||||
sig3 = (f64 [%f10], i32 [0], i32 [4]) -> f64 [%f10] native
|
||||
; check: sig3 = (f64 [%f10], i32 [0], i32 [4]) -> f64 [%f10] native
|
||||
sig3 = (f64 [%f10], i32 [0], i32 [4]) -> f64 [%f10] system_v
|
||||
; check: sig3 = (f64 [%f10], i32 [0], i32 [4]) -> f64 [%f10] system_v
|
||||
|
||||
; Stack argument before register argument
|
||||
sig4 = (f32 [72], i32 [%x10]) native
|
||||
; check: sig4 = (f32 [72], i32 [%x10]) native
|
||||
sig4 = (f32 [72], i32 [%x10]) system_v
|
||||
; check: sig4 = (f32 [72], i32 [%x10]) system_v
|
||||
|
||||
; Return value on stack
|
||||
sig5 = () -> f32 [0] native
|
||||
; check: sig5 = () -> f32 [0] native
|
||||
sig5 = () -> f32 [0] system_v
|
||||
; check: sig5 = () -> f32 [0] system_v
|
||||
|
||||
; function + signature
|
||||
fn0 = function %bar(i32 [%x10]) -> b1 [%x10] native
|
||||
; check: sig6 = (i32 [%x10]) -> b1 [%x10] native
|
||||
fn0 = function %bar(i32 [%x10]) -> b1 [%x10] system_v
|
||||
; check: sig6 = (i32 [%x10]) -> b1 [%x10] system_v
|
||||
; nextln: fn0 = sig6 %bar
|
||||
|
||||
ebb0(v0: i32):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
test licm
|
||||
|
||||
function %complex(i32) -> i32 native {
|
||||
function %complex(i32) -> i32 system_v {
|
||||
ebb0(v0: i32):
|
||||
jump ebb1(v0)
|
||||
|
||||
|
||||
81
cranelift/filetests/licm/reject.cton
Normal file
81
cranelift/filetests/licm/reject.cton
Normal file
@@ -0,0 +1,81 @@
|
||||
test licm
|
||||
|
||||
function %other_side_effects(i32) -> i32 {
|
||||
|
||||
ebb0(v0: i32):
|
||||
jump ebb1(v0)
|
||||
|
||||
ebb1(v1: i32):
|
||||
regmove.i32 v0, %10 -> %20
|
||||
; check: ebb1(v1: i32):
|
||||
; check: regmove.i32 v0, %10 -> %20
|
||||
v2 = iconst.i32 1
|
||||
brz v1, ebb2(v1)
|
||||
v5 = isub v1, v2
|
||||
jump ebb1(v5)
|
||||
|
||||
ebb2(v6: i32):
|
||||
return v6
|
||||
|
||||
}
|
||||
|
||||
function %cpu_flags(i32, i32) -> i32 {
|
||||
ebb0(v0: i32, v1: i32):
|
||||
jump ebb1(v0, v1)
|
||||
|
||||
ebb1(v2: i32, v3: i32):
|
||||
v4 = ifcmp.i32 v0, v1
|
||||
v5 = selectif.i32 eq v4, v2, v3
|
||||
; check: ebb1(v2: i32, v3: i32):
|
||||
; check: ifcmp.i32 v0, v1
|
||||
; check: v5 = selectif.i32 eq v4, v2, v3
|
||||
v8 = iconst.i32 1
|
||||
brz v1, ebb2(v1)
|
||||
v9 = isub v1, v8
|
||||
v10 = iadd v1, v8
|
||||
jump ebb1(v9, v10)
|
||||
|
||||
ebb2(v6: i32):
|
||||
return v6
|
||||
}
|
||||
|
||||
function %spill(i32, i32) -> i32 {
|
||||
ebb0(v0: i32, v1: i32):
|
||||
v2 = spill.i32 v0
|
||||
jump ebb1(v0, v1)
|
||||
|
||||
ebb1(v3: i32, v4: i32):
|
||||
v5 = spill.i32 v1
|
||||
v6 = fill.i32 v2
|
||||
v7 = fill.i32 v5
|
||||
; check: ebb1(v3: i32, v4: i32):
|
||||
; check: v5 = spill.i32 v1
|
||||
; check: v6 = fill.i32 v2
|
||||
; check: v7 = fill v5
|
||||
brz v1, ebb2(v1)
|
||||
v9 = isub v1, v4
|
||||
jump ebb1(v9, v3)
|
||||
|
||||
ebb2(v10: i32):
|
||||
return v10
|
||||
}
|
||||
|
||||
function %non_invariant_aliases(i32) -> i32 {
|
||||
|
||||
ebb0(v0: i32):
|
||||
jump ebb1(v0)
|
||||
|
||||
ebb1(v1: i32):
|
||||
v8 -> v1
|
||||
v9 -> v1
|
||||
v2 = iadd v8, v9
|
||||
; check: ebb1(v1: i32):
|
||||
; check: v2 = iadd v8, v9
|
||||
brz v1, ebb2(v1)
|
||||
v5 = isub v1, v2
|
||||
jump ebb1(v5)
|
||||
|
||||
ebb2(v6: i32):
|
||||
return v6
|
||||
|
||||
}
|
||||
@@ -9,7 +9,7 @@ ebb0:
|
||||
ebb1:
|
||||
jump ebb0()
|
||||
}
|
||||
; sameln: function %minimal() native {
|
||||
; sameln: function %minimal() system_v {
|
||||
; nextln: ebb0:
|
||||
; nextln: jump ebb1
|
||||
; nextln:
|
||||
@@ -25,7 +25,7 @@ ebb0(v90: i32):
|
||||
ebb1(v91: i32):
|
||||
jump ebb0(v91)
|
||||
}
|
||||
; sameln: function %onearg(i32) native {
|
||||
; sameln: function %onearg(i32) system_v {
|
||||
; nextln: ebb0(v90: i32):
|
||||
; nextln: jump ebb1(v90)
|
||||
; nextln:
|
||||
@@ -41,7 +41,7 @@ ebb0(v90: i32, v91: f32):
|
||||
ebb1(v92: i32, v93: f32):
|
||||
jump ebb0(v92, v93)
|
||||
}
|
||||
; sameln: function %twoargs(i32, f32) native {
|
||||
; sameln: function %twoargs(i32, f32) system_v {
|
||||
; nextln: ebb0(v90: i32, v91: f32):
|
||||
; nextln: jump ebb1(v90, v91)
|
||||
; nextln:
|
||||
@@ -57,7 +57,7 @@ ebb0(v90: i32):
|
||||
ebb1:
|
||||
brnz v90, ebb1()
|
||||
}
|
||||
; sameln: function %minimal(i32) native {
|
||||
; sameln: function %minimal(i32) system_v {
|
||||
; nextln: ebb0(v90: i32):
|
||||
; nextln: brz v90, ebb1
|
||||
; nextln:
|
||||
@@ -72,7 +72,7 @@ ebb0(v90: i32, v91: f32):
|
||||
ebb1(v92: i32, v93: f32):
|
||||
brnz v90, ebb0(v92, v93)
|
||||
}
|
||||
; sameln: function %twoargs(i32, f32) native {
|
||||
; sameln: function %twoargs(i32, f32) system_v {
|
||||
; nextln: ebb0(v90: i32, v91: f32):
|
||||
; nextln: brz v90, ebb1(v90, v91)
|
||||
; nextln:
|
||||
@@ -94,7 +94,7 @@ ebb30:
|
||||
ebb40:
|
||||
trap user4
|
||||
}
|
||||
; sameln: function %jumptable(i32) native {
|
||||
; sameln: function %jumptable(i32) system_v {
|
||||
; check: jt2 = jump_table 0, 0, ebb10, ebb40, ebb20, ebb30
|
||||
; check: jt200 = jump_table 0
|
||||
; check: ebb10(v3: i32):
|
||||
|
||||
@@ -5,7 +5,7 @@ function %mini() {
|
||||
ebb1:
|
||||
return
|
||||
}
|
||||
; sameln: function %mini() native {
|
||||
; sameln: function %mini() system_v {
|
||||
; nextln: ebb1:
|
||||
; nextln: return
|
||||
; nextln: }
|
||||
@@ -29,10 +29,10 @@ function %signatures() {
|
||||
fn5 = sig11 %foo
|
||||
fn8 = function %bar(i32) -> b1
|
||||
}
|
||||
; sameln: function %signatures() native {
|
||||
; check: sig10 = () native
|
||||
; sameln: function %signatures() system_v {
|
||||
; check: sig10 = () system_v
|
||||
; check: sig11 = (i32, f64) -> i32, b1 spiderwasm
|
||||
; check: sig12 = (i32) -> b1 native
|
||||
; check: sig12 = (i32) -> b1 system_v
|
||||
; not: fn0
|
||||
; check: fn5 = sig11 %foo
|
||||
; check: fn8 = sig12 %bar
|
||||
@@ -88,7 +88,7 @@ function %special1(i32 sret, i32 fp, i32 csr, i32 link) -> i32 link, i32 fp, i32
|
||||
ebb0(v1: i32, v2: i32, v3: i32, v4: i32):
|
||||
return v4, v2, v3, v1
|
||||
}
|
||||
; check: function %special1(i32 sret, i32 fp, i32 csr, i32 link) -> i32 link, i32 fp, i32 csr, i32 sret native {
|
||||
; check: function %special1(i32 sret, i32 fp, i32 csr, i32 link) -> i32 link, i32 fp, i32 csr, i32 sret system_v {
|
||||
; check: ebb0(v1: i32, v2: i32, v3: i32, v4: i32):
|
||||
; check: return v4, v2, v3, v1
|
||||
; check: }
|
||||
|
||||
@@ -13,7 +13,7 @@ ebb1(v0: i32 [%x8], v1: i32):
|
||||
@55 v9 = iadd v8, v7
|
||||
@a5 [Iret#5] return v0, v8
|
||||
}
|
||||
; sameln: function %foo(i32, i32) native {
|
||||
; sameln: function %foo(i32, i32) system_v {
|
||||
; nextln: ebb1(v0: i32 [%x8], v1: i32):
|
||||
; nextln: [-,-]$WS v2 = iadd v0, v1
|
||||
; nextln: [-]$WS trap heap_oob
|
||||
|
||||
@@ -2,4 +2,4 @@ test cat
|
||||
|
||||
; 'function' is not a keyword, and can be used as the name of a function too.
|
||||
function %function() {}
|
||||
; check: function %function() native
|
||||
; check: function %function() system_v
|
||||
|
||||
@@ -9,7 +9,7 @@ ebb100(v20: i32):
|
||||
v9200 = f64const 0x4.0p0
|
||||
trap user4
|
||||
}
|
||||
; sameln: function %defs() native {
|
||||
; sameln: function %defs() system_v {
|
||||
; nextln: ebb100(v20: i32):
|
||||
; nextln: v1000 = iconst.i32x8 5
|
||||
; nextln: v9200 = f64const 0x1.0000000000000p2
|
||||
@@ -23,7 +23,7 @@ ebb100(v20: i32):
|
||||
v200 = iadd v20, v1000
|
||||
jump ebb100(v1000)
|
||||
}
|
||||
; sameln: function %use_value() native {
|
||||
; sameln: function %use_value() system_v {
|
||||
; nextln: ebb100(v20: i32):
|
||||
; nextln: v1000 = iadd_imm v20, 5
|
||||
; nextln: v200 = iadd v20, v1000
|
||||
|
||||
@@ -5,7 +5,7 @@ function %minimal() {
|
||||
ebb0:
|
||||
trap user0
|
||||
}
|
||||
; sameln: function %minimal() native {
|
||||
; sameln: function %minimal() system_v {
|
||||
; nextln: ebb0:
|
||||
; nextln: trap user0
|
||||
; nextln: }
|
||||
@@ -18,7 +18,7 @@ ebb0:
|
||||
v1 = iconst.i8 6
|
||||
v2 = ishl v0, v1
|
||||
}
|
||||
; sameln: function %ivalues() native {
|
||||
; sameln: function %ivalues() system_v {
|
||||
; nextln: ebb0:
|
||||
; nextln: v0 = iconst.i32 2
|
||||
; nextln: v1 = iconst.i8 6
|
||||
@@ -34,7 +34,7 @@ ebb0:
|
||||
v2 = bextend.b32 v1
|
||||
v3 = bxor v0, v2
|
||||
}
|
||||
; sameln: function %bvalues() native {
|
||||
; sameln: function %bvalues() system_v {
|
||||
; nextln: ebb0:
|
||||
; nextln: v0 = bconst.b32 true
|
||||
; nextln: v1 = bconst.b8 false
|
||||
@@ -47,17 +47,17 @@ function %select() {
|
||||
ebb0(v90: i32, v91: i32, v92: b1):
|
||||
v0 = select v92, v90, v91
|
||||
}
|
||||
; sameln: function %select() native {
|
||||
; sameln: function %select() system_v {
|
||||
; nextln: ebb0(v90: i32, v91: i32, v92: b1):
|
||||
; nextln: v0 = select v92, v90, v91
|
||||
; nextln: }
|
||||
|
||||
; Polymorphic instruction controlled by third operand.
|
||||
function %selectif() native {
|
||||
function %selectif() system_v {
|
||||
ebb0(v95: i32, v96: i32, v97: b1):
|
||||
v98 = selectif.i32 eq v97, v95, v96
|
||||
}
|
||||
; sameln: function %selectif() native {
|
||||
; sameln: function %selectif() system_v {
|
||||
; nextln: ebb0(v95: i32, v96: i32, v97: b1):
|
||||
; nextln: v98 = selectif.i32 eq v97, v95, v96
|
||||
; nextln: }
|
||||
@@ -69,7 +69,7 @@ ebb0:
|
||||
v1 = extractlane v0, 3
|
||||
v2 = insertlane v0, 1, v1
|
||||
}
|
||||
; sameln: function %lanes() native {
|
||||
; sameln: function %lanes() system_v {
|
||||
; nextln: ebb0:
|
||||
; nextln: v0 = iconst.i32x4 2
|
||||
; nextln: v1 = extractlane v0, 3
|
||||
@@ -85,7 +85,7 @@ ebb0(v90: i32, v91: i32):
|
||||
v3 = irsub_imm v91, 45
|
||||
br_icmp eq v90, v91, ebb0(v91, v90)
|
||||
}
|
||||
; sameln: function %icmp(i32, i32) native {
|
||||
; sameln: function %icmp(i32, i32) system_v {
|
||||
; nextln: ebb0(v90: i32, v91: i32):
|
||||
; nextln: v0 = icmp eq v90, v91
|
||||
; nextln: v1 = icmp ult v90, v91
|
||||
@@ -101,7 +101,7 @@ ebb0(v90: f32, v91: f32):
|
||||
v1 = fcmp uno v90, v91
|
||||
v2 = fcmp lt v90, v91
|
||||
}
|
||||
; sameln: function %fcmp(f32, f32) native {
|
||||
; sameln: function %fcmp(f32, f32) system_v {
|
||||
; nextln: ebb0(v90: f32, v91: f32):
|
||||
; nextln: v0 = fcmp eq v90, v91
|
||||
; nextln: v1 = fcmp uno v90, v91
|
||||
@@ -115,7 +115,7 @@ ebb0(v90: i32, v91: f32):
|
||||
v0 = bitcast.i8x4 v90
|
||||
v1 = bitcast.i32 v91
|
||||
}
|
||||
; sameln: function %bitcast(i32, f32) native {
|
||||
; sameln: function %bitcast(i32, f32) system_v {
|
||||
; nextln: ebb0(v90: i32, v91: f32):
|
||||
; nextln: v0 = bitcast.i8x4 v90
|
||||
; nextln: v1 = bitcast.i32 v91
|
||||
@@ -135,7 +135,7 @@ ebb0:
|
||||
stack_store v1, ss10+2
|
||||
stack_store v2, ss2
|
||||
}
|
||||
; sameln: function %stack() native {
|
||||
; sameln: function %stack() system_v {
|
||||
; check: ss2 = explicit_slot 4
|
||||
; check: ss3 = incoming_arg 4, offset 8
|
||||
; check: ss4 = outgoing_arg 4
|
||||
@@ -162,7 +162,7 @@ ebb0(v1: i32):
|
||||
store aligned v3, v1+12
|
||||
store notrap aligned v3, v1-12
|
||||
}
|
||||
; sameln: function %memory(i32) native {
|
||||
; sameln: function %memory(i32) system_v {
|
||||
; nextln: ebb0(v1: i32):
|
||||
; nextln: v2 = load.i64 v1
|
||||
; nextln: v3 = load.i64 aligned v1
|
||||
@@ -187,7 +187,7 @@ ebb0(v1: i32):
|
||||
regfill v1, ss0 -> %10
|
||||
return
|
||||
}
|
||||
; sameln: function %diversion(i32) native {
|
||||
; sameln: function %diversion(i32) system_v {
|
||||
; nextln: ss0 = spill_slot 4
|
||||
; check: ebb0(v1: i32):
|
||||
; nextln: regmove v1, %10 -> %20
|
||||
@@ -204,7 +204,7 @@ ebb0:
|
||||
copy_special %20 -> %10
|
||||
return
|
||||
}
|
||||
; sameln: function %copy_special() native {
|
||||
; sameln: function %copy_special() system_v {
|
||||
; nextln: ebb0:
|
||||
; nextln: copy_special %10 -> %20
|
||||
; nextln: copy_special %20 -> %10
|
||||
|
||||
100
cranelift/filetests/postopt/basic.cton
Normal file
100
cranelift/filetests/postopt/basic.cton
Normal file
@@ -0,0 +1,100 @@
|
||||
test postopt
|
||||
isa intel
|
||||
|
||||
; Test that compare+branch sequences are folded effectively on x86.
|
||||
|
||||
function %br_icmp(i32, i32) -> i32 {
|
||||
ebb0(v0: i32, v1: i32):
|
||||
[Op1icscc#39,%rdx] v2 = icmp slt v0, v1
|
||||
[Op1t8jccd_long#85] brnz v2, ebb1
|
||||
[Op1ret#c3] return v1
|
||||
|
||||
ebb1:
|
||||
[Op1puid#b8,%rax] v8 = iconst.i32 3
|
||||
[Op1ret#c3] return v8
|
||||
}
|
||||
; sameln: function %br_icmp
|
||||
; nextln: ebb0(v0: i32, v1: i32):
|
||||
; nextln: v9 = ifcmp v0, v1
|
||||
; nextln: v2 = trueif slt v9
|
||||
; nextln: brif slt v9, ebb1
|
||||
; nextln: return v1
|
||||
; nextln:
|
||||
; nextln: ebb1:
|
||||
; nextln: v8 = iconst.i32 3
|
||||
; nextln: return v8
|
||||
; nextln: }
|
||||
|
||||
; Use brz instead of brnz, so the condition is inverted.
|
||||
|
||||
function %br_icmp_inverse(i32, i32) -> i32 {
|
||||
ebb0(v0: i32, v1: i32):
|
||||
[Op1icscc#39,%rdx] v2 = icmp slt v0, v1
|
||||
[Op1t8jccd_long#84] brz v2, ebb1
|
||||
[Op1ret#c3] return v1
|
||||
|
||||
ebb1:
|
||||
[Op1puid#b8,%rax] v8 = iconst.i32 3
|
||||
[Op1ret#c3] return v8
|
||||
}
|
||||
; sameln: function %br_icmp_inverse
|
||||
; nextln: ebb0(v0: i32, v1: i32):
|
||||
; nextln: v9 = ifcmp v0, v1
|
||||
; nextln: v2 = trueif slt v9
|
||||
; nextln: brif sge v9, ebb1
|
||||
; nextln: return v1
|
||||
; nextln:
|
||||
; nextln: ebb1:
|
||||
; nextln: v8 = iconst.i32 3
|
||||
; nextln: return v8
|
||||
; nextln: }
|
||||
|
||||
; Use icmp_imm instead of icmp.
|
||||
|
||||
function %br_icmp_imm(i32, i32) -> i32 {
|
||||
ebb0(v0: i32, v1: i32):
|
||||
[Op1icsccib#7083] v2 = icmp_imm slt v0, 2
|
||||
[Op1t8jccd_long#84] brz v2, ebb1
|
||||
[Op1ret#c3] return v1
|
||||
|
||||
ebb1:
|
||||
[Op1puid#b8,%rax] v8 = iconst.i32 3
|
||||
[Op1ret#c3] return v8
|
||||
}
|
||||
; sameln: function %br_icmp_imm
|
||||
; nextln: ebb0(v0: i32, v1: i32):
|
||||
; nextln: v9 = ifcmp_imm v0, 2
|
||||
; nextln: v2 = trueif slt v9
|
||||
; nextln: brif sge v9, ebb1
|
||||
; nextln: return v1
|
||||
; nextln:
|
||||
; nextln: ebb1:
|
||||
; nextln: v8 = iconst.i32 3
|
||||
; nextln: return v8
|
||||
; nextln: }
|
||||
|
||||
; Use fcmp instead of icmp.
|
||||
|
||||
function %br_fcmp(f32, f32) -> f32 {
|
||||
ebb0(v0: f32, v1: f32):
|
||||
[Op2fcscc#42e,%rdx] v2 = fcmp gt v0, v1
|
||||
[Op1t8jccd_long#84] brz v2, ebb1
|
||||
[Op1ret#c3] return v1
|
||||
|
||||
ebb1:
|
||||
[Op1puid#b8,%rax] v18 = iconst.i32 0x40a8_0000
|
||||
[Mp2frurm#56e,%xmm0] v8 = bitcast.f32 v18
|
||||
[Op1ret#c3] return v8
|
||||
}
|
||||
; sameln: function %br_fcmp
|
||||
; nextln: ebb0(v0: f32, v1: f32):
|
||||
; nextln: v19 = ffcmp v0, v1
|
||||
; nextln: v2 = trueff gt v19
|
||||
; nextln: brff ule v19, ebb1
|
||||
; nextln: return v1
|
||||
; nextln:
|
||||
; nextln: ebb1:
|
||||
; nextln: v18 = iconst.i32 0x40a8_0000
|
||||
; nextln: v8 = bitcast.f32 v18
|
||||
; nextln: return v8
|
||||
; nextln: }
|
||||
80
cranelift/filetests/preopt/simplify.cton
Normal file
80
cranelift/filetests/preopt/simplify.cton
Normal file
@@ -0,0 +1,80 @@
|
||||
test preopt
|
||||
isa intel
|
||||
|
||||
function %iadd_imm(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
v1 = iconst.i32 2
|
||||
v2 = iadd v0, v1
|
||||
return v2
|
||||
}
|
||||
; sameln: function %iadd_imm
|
||||
; nextln: ebb0(v0: i32):
|
||||
; nextln: v1 = iconst.i32 2
|
||||
; nextln: v2 = iadd_imm v0, 2
|
||||
; nextln: return v2
|
||||
; nextln: }
|
||||
|
||||
function %isub_imm(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
v1 = iconst.i32 2
|
||||
v2 = isub v0, v1
|
||||
return v2
|
||||
}
|
||||
; sameln: function %isub_imm
|
||||
; nextln: ebb0(v0: i32):
|
||||
; nextln: v1 = iconst.i32 2
|
||||
; nextln: v2 = iadd_imm v0, -2
|
||||
; nextln: return v2
|
||||
; nextln: }
|
||||
|
||||
function %icmp_imm(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
v1 = iconst.i32 2
|
||||
v2 = icmp slt v0, v1
|
||||
v3 = bint.i32 v2
|
||||
return v3
|
||||
}
|
||||
; sameln: function %icmp_imm
|
||||
; nextln: ebb0(v0: i32):
|
||||
; nextln: v1 = iconst.i32 2
|
||||
; nextln: v2 = icmp_imm slt v0, 2
|
||||
; nextln: v3 = bint.i32 v2
|
||||
; nextln: return v3
|
||||
; nextln: }
|
||||
|
||||
function %brz_bint(i32) {
|
||||
ebb0(v0: i32):
|
||||
v3 = icmp_imm slt v0, 0
|
||||
v1 = bint.i32 v3
|
||||
v2 = select v1, v1, v1
|
||||
trapz v1, user0
|
||||
brz v1, ebb1
|
||||
jump ebb2
|
||||
|
||||
ebb1:
|
||||
return
|
||||
|
||||
ebb2:
|
||||
return
|
||||
}
|
||||
; sameln: function %brz_bint
|
||||
; nextln: (v0: i32):
|
||||
; nextln: v3 = icmp_imm slt v0, 0
|
||||
; nextln: v1 = bint.i32 v3
|
||||
; nextln: v2 = select v3, v1, v1
|
||||
; nextln: trapz v3, user0
|
||||
; nextln: brz v3, ebb1
|
||||
; nextln: jump ebb2
|
||||
|
||||
function %irsub_imm(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
v1 = iconst.i32 2
|
||||
v2 = isub v1, v0
|
||||
return v2
|
||||
}
|
||||
; sameln: function %irsub_imm
|
||||
; nextln: ebb0(v0: i32):
|
||||
; nextln: v1 = iconst.i32 2
|
||||
; nextln: v2 = irsub_imm v1, 2
|
||||
; nextln: return v2
|
||||
; nextln: }
|
||||
@@ -109,7 +109,7 @@ ebb1(v10: i32):
|
||||
return v11
|
||||
}
|
||||
|
||||
function %gvn_unremovable_phi(i32) native {
|
||||
function %gvn_unremovable_phi(i32) system_v {
|
||||
ebb0(v0: i32):
|
||||
v2 = iconst.i32 0
|
||||
jump ebb2(v2, v0)
|
||||
|
||||
@@ -5,12 +5,12 @@ isa intel haswell
|
||||
; Reported as https://github.com/Cretonne/cretonne/issues/207
|
||||
;
|
||||
; The coalescer creates a virtual register with two interfering values.
|
||||
function %pr207(i64 vmctx, i32, i32) -> i32 native {
|
||||
function %pr207(i64 vmctx, i32, i32) -> i32 system_v {
|
||||
gv0 = vmctx-8
|
||||
heap0 = static gv0, min 0, bound 0x5000, guard 0x0040_0000
|
||||
sig0 = (i64 vmctx, i32, i32) -> i32 native
|
||||
sig1 = (i64 vmctx, i32, i32, i32) -> i32 native
|
||||
sig2 = (i64 vmctx, i32, i32, i32) -> i32 native
|
||||
sig0 = (i64 vmctx, i32, i32) -> i32 system_v
|
||||
sig1 = (i64 vmctx, i32, i32, i32) -> i32 system_v
|
||||
sig2 = (i64 vmctx, i32, i32, i32) -> i32 system_v
|
||||
fn0 = sig0 u0:2
|
||||
fn1 = sig1 u0:0
|
||||
fn2 = sig2 u0:1
|
||||
@@ -1034,10 +1034,10 @@ ebb92(v767: i32):
|
||||
}
|
||||
|
||||
; Same problem from musl.wasm.
|
||||
function %musl(f64 [%xmm0], i64 vmctx [%rdi]) -> f64 [%xmm0] native {
|
||||
function %musl(f64 [%xmm0], i64 vmctx [%rdi]) -> f64 [%xmm0] system_v {
|
||||
gv0 = vmctx
|
||||
heap0 = static gv0, min 0, bound 0x0001_0000_0000, guard 0x8000_0000
|
||||
sig0 = (f64 [%xmm0], i32 [%rdi], i64 vmctx [%rsi]) -> f64 [%xmm0] native
|
||||
sig0 = (f64 [%xmm0], i32 [%rdi], i64 vmctx [%rsi]) -> f64 [%xmm0] system_v
|
||||
fn0 = sig0 u0:517
|
||||
|
||||
ebb0(v0: f64, v1: i64):
|
||||
|
||||
@@ -5,7 +5,7 @@ isa intel haswell
|
||||
; Reported as https://github.com/Cretonne/cretonne/issues/216 from the Binaryen fuzzer.
|
||||
;
|
||||
; The (old) coalescer creates a virtual register with two identical values.
|
||||
function %pr216(i32 [%rdi], i64 vmctx [%rsi]) -> i64 [%rax] native {
|
||||
function %pr216(i32 [%rdi], i64 vmctx [%rsi]) -> i64 [%rax] system_v {
|
||||
ebb0(v0: i32, v1: i64):
|
||||
v3 = iconst.i64 0
|
||||
v5 = iconst.i32 0
|
||||
|
||||
@@ -2,7 +2,7 @@ test regalloc
|
||||
set is_64bit
|
||||
isa intel haswell
|
||||
|
||||
function %pr227(i32 [%rdi], i32 [%rsi], i32 [%rdx], i32 [%rcx], i64 vmctx [%r8]) native {
|
||||
function %pr227(i32 [%rdi], i32 [%rsi], i32 [%rdx], i32 [%rcx], i64 vmctx [%r8]) system_v {
|
||||
gv0 = vmctx
|
||||
heap0 = static gv0, min 0, bound 0x0001_0000_0000, guard 0x8000_0000
|
||||
|
||||
@@ -21,7 +21,7 @@ function %pr227(i32 [%rdi], i32 [%rsi], i32 [%rdx], i32 [%rcx], i64 vmctx [%r8])
|
||||
@0011 [RexOp1puid#b8] v9 = iconst.i32 0
|
||||
@0015 [RexOp1puid#b8] v11 = iconst.i32 0
|
||||
@0017 [RexOp1icscc#39] v12 = icmp.i32 eq v15, v11
|
||||
@0017 [RexOp2urm#4b6] v13 = bint.i32 v12
|
||||
@0017 [RexOp2urm_noflags#4b6] v13 = bint.i32 v12
|
||||
@001a [RexOp1rr#21] v14 = band v9, v13
|
||||
@001b [RexOp1tjccb#75] brnz v14, ebb6
|
||||
@001d [RexOp1jmpb#eb] jump ebb7
|
||||
|
||||
@@ -9,7 +9,7 @@ isa intel haswell
|
||||
;
|
||||
; Test case by binaryen fuzzer!
|
||||
|
||||
function %pr215(i64 vmctx [%rdi]) native {
|
||||
function %pr215(i64 vmctx [%rdi]) system_v {
|
||||
ebb0(v0: i64):
|
||||
v10 = iconst.i64 0
|
||||
v1 = bitcast.f64 v10
|
||||
|
||||
@@ -2,7 +2,7 @@ test regalloc
|
||||
set is_64bit=1
|
||||
isa intel haswell
|
||||
|
||||
function %foo() native {
|
||||
function %foo() system_v {
|
||||
ebb4:
|
||||
v3 = iconst.i32 0
|
||||
jump ebb3
|
||||
|
||||
@@ -11,7 +11,7 @@ isa intel
|
||||
; This ended up confusong the constraint solver which had not made a record of
|
||||
; the fixed register assignment for v9 since it was already in the correct
|
||||
; register.
|
||||
function %pr147(i32) -> i32 native {
|
||||
function %pr147(i32) -> i32 system_v {
|
||||
ebb0(v0: i32):
|
||||
v1 = iconst.i32 0
|
||||
v2 = iconst.i32 1
|
||||
|
||||
@@ -2,7 +2,7 @@ test regalloc
|
||||
set is_64bit=1
|
||||
isa intel haswell
|
||||
|
||||
function %test(i64) -> i64 native {
|
||||
function %test(i64) -> i64 system_v {
|
||||
ebb0(v0: i64):
|
||||
v2 = iconst.i64 12
|
||||
; This division clobbers two of its fixed input registers on Intel.
|
||||
|
||||
@@ -11,11 +11,11 @@ isa intel haswell
|
||||
;
|
||||
; The problem was the reload pass rewriting EBB arguments on "brnz v9, ebb3(v9)"
|
||||
|
||||
function %pr208(i64 vmctx [%rdi]) native {
|
||||
function %pr208(i64 vmctx [%rdi]) system_v {
|
||||
gv0 = vmctx-8
|
||||
heap0 = static gv0, min 0, bound 0x5000, guard 0x0040_0000
|
||||
sig0 = (i64 vmctx [%rdi]) -> i32 [%rax] native
|
||||
sig1 = (i64 vmctx [%rdi], i32 [%rsi]) native
|
||||
sig0 = (i64 vmctx [%rdi]) -> i32 [%rax] system_v
|
||||
sig1 = (i64 vmctx [%rdi], i32 [%rsi]) system_v
|
||||
fn0 = sig0 u0:1
|
||||
fn1 = sig1 u0:3
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ isa riscv enable_e
|
||||
|
||||
; Check that we can handle a function return value that got spilled.
|
||||
function %spill_return() -> i32 {
|
||||
fn0 = function %foo() -> i32 native
|
||||
fn0 = function %foo() -> i32 system_v
|
||||
|
||||
ebb0:
|
||||
v0 = call fn0()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
test regalloc
|
||||
isa intel haswell
|
||||
|
||||
function %pr165() native {
|
||||
function %pr165() system_v {
|
||||
ebb0:
|
||||
v0 = iconst.i32 0x0102_0304
|
||||
v1 = iconst.i32 0x1102_0304
|
||||
@@ -19,7 +19,7 @@ ebb0:
|
||||
|
||||
; Same as above, but use so many registers that spilling is required.
|
||||
; Note: This is also a candidate for using xchg instructions.
|
||||
function %emergency_spill() native {
|
||||
function %emergency_spill() system_v {
|
||||
ebb0:
|
||||
v0 = iconst.i32 0x0102_0304
|
||||
v1 = iconst.i32 0x1102_0304
|
||||
|
||||
@@ -13,7 +13,7 @@ isa intel
|
||||
;
|
||||
; The spiller was not releasing register pressure for dead EBB parameters.
|
||||
|
||||
function %pr223(i32 [%rdi], i64 vmctx [%rsi]) -> i64 [%rax] native {
|
||||
function %pr223(i32 [%rdi], i64 vmctx [%rsi]) -> i64 [%rax] system_v {
|
||||
ebb0(v0: i32, v1: i64):
|
||||
v2 = iconst.i32 0
|
||||
v3 = iconst.i64 0
|
||||
|
||||
@@ -93,7 +93,7 @@ ebb0(v0: i32):
|
||||
|
||||
; The same value used as indirect callee and argument.
|
||||
function %doubleuse_icall1(i32) {
|
||||
sig0 = (i32) native
|
||||
sig0 = (i32) system_v
|
||||
ebb0(v0: i32):
|
||||
; not:copy
|
||||
call_indirect sig0, v0(v0)
|
||||
@@ -102,7 +102,7 @@ ebb0(v0: i32):
|
||||
|
||||
; The same value used as indirect callee and two arguments.
|
||||
function %doubleuse_icall2(i32) {
|
||||
sig0 = (i32, i32) native
|
||||
sig0 = (i32, i32) system_v
|
||||
ebb0(v0: i32):
|
||||
; check: $(c=$V) = copy v0
|
||||
call_indirect sig0, v0(v0, v0)
|
||||
|
||||
16
cranelift/filetests/verifier/defs_dominates_uses.cton
Normal file
16
cranelift/filetests/verifier/defs_dominates_uses.cton
Normal file
@@ -0,0 +1,16 @@
|
||||
test verifier
|
||||
|
||||
; Test verification that uses properly dominate defs.
|
||||
|
||||
function %non_dominating(i32) -> i32 system_v {
|
||||
ebb0(v0: i32):
|
||||
v1 = iadd.i32 v2, v0 ; error: uses value from non-dominating
|
||||
v2 = iadd.i32 v1, v0
|
||||
return v2
|
||||
}
|
||||
|
||||
function %inst_uses_its_own_values(i32) -> i32 system_v {
|
||||
ebb0(v0: i32):
|
||||
v1 = iadd.i32 v1, v0 ; error: uses value from itself
|
||||
return v1
|
||||
}
|
||||
@@ -4,65 +4,65 @@ isa intel
|
||||
; Simple, correct use of CPU flags.
|
||||
function %simple(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op2urm_abcd#4b6] v3 = bint.i32 v2
|
||||
[Op1ret#c3] return v3
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op2urm_noflags_abcd#4b6] v3 = bint.i32 v2
|
||||
[Op1ret#c3] return v3
|
||||
}
|
||||
|
||||
; Overlapping flag values of different types.
|
||||
function %overlap(i32, f32) -> i32 {
|
||||
ebb0(v0: i32, v1: f32):
|
||||
[Op1rcmp#39] v2 = ifcmp v0, v0
|
||||
[Op2fcmp#42e] v3 = ffcmp v1, v1
|
||||
[Op2setf_abcd#490] v4 = trueff gt v3 ; error: conflicting live CPU flags: v2 and v3
|
||||
[Op2seti_abcd#490] v5 = trueif ugt v2
|
||||
[Op1rr#21] v6 = band v4, v5
|
||||
[Op2urm_abcd#4b6] v7 = bint.i32 v6
|
||||
[Op1ret#c3] return v7
|
||||
[Op1rcmp#39] v2 = ifcmp v0, v0
|
||||
[Op2fcmp#42e] v3 = ffcmp v1, v1
|
||||
[Op2setf_abcd#490] v4 = trueff gt v3 ; error: conflicting live CPU flags: v2 and v3
|
||||
[Op2seti_abcd#490] v5 = trueif ugt v2
|
||||
[Op1rr#21] v6 = band v4, v5
|
||||
[Op2urm_noflags_abcd#4b6] v7 = bint.i32 v6
|
||||
[Op1ret#c3] return v7
|
||||
}
|
||||
|
||||
; CPU flags clobbered by arithmetic.
|
||||
function %clobbered(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1rr#01] v2 = iadd v0, v0 ; error: encoding clobbers live CPU flags in v1
|
||||
[Op2seti_abcd#490] v3 = trueif ugt v1
|
||||
[Op2urm_abcd#4b6] v4 = bint.i32 v3
|
||||
[Op1ret#c3] return v4
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1rr#01] v2 = iadd v0, v0 ; error: encoding clobbers live CPU flags in v1
|
||||
[Op2seti_abcd#490] v3 = trueif ugt v1
|
||||
[Op2urm_noflags_abcd#4b6] v4 = bint.i32 v3
|
||||
[Op1ret#c3] return v4
|
||||
}
|
||||
|
||||
; CPU flags not clobbered by load.
|
||||
function %live_across_load(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1ld#8b] v2 = load.i32 v0
|
||||
[Op2seti_abcd#490] v3 = trueif ugt v1
|
||||
[Op2urm_abcd#4b6] v4 = bint.i32 v3
|
||||
[Op1ret#c3] return v4
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1ld#8b] v2 = load.i32 v0
|
||||
[Op2seti_abcd#490] v3 = trueif ugt v1
|
||||
[Op2urm_noflags_abcd#4b6] v4 = bint.i32 v3
|
||||
[Op1ret#c3] return v4
|
||||
}
|
||||
|
||||
; Correct use of CPU flags across EBB.
|
||||
function %live_across_ebb(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1jmpb#eb] jump ebb1
|
||||
ebb1:
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op2urm_abcd#4b6] v3 = bint.i32 v2
|
||||
[Op1ret#c3] return v3
|
||||
ebb0(v0: i32):
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1jmpb#eb] jump ebb1
|
||||
ebb1:
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op2urm_noflags_abcd#4b6] v3 = bint.i32 v2
|
||||
[Op1ret#c3] return v3
|
||||
}
|
||||
|
||||
function %live_across_ebb_backwards(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
[Op1jmpb#eb] jump ebb2
|
||||
ebb1:
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op2urm_abcd#4b6] v3 = bint.i32 v2
|
||||
[Op1ret#c3] return v3
|
||||
ebb2:
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1jmpb#eb] jump ebb1
|
||||
ebb0(v0: i32):
|
||||
[Op1jmpb#eb] jump ebb2
|
||||
ebb1:
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op2urm_noflags_abcd#4b6] v3 = bint.i32 v2
|
||||
[Op1ret#c3] return v3
|
||||
ebb2:
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1jmpb#eb] jump ebb1
|
||||
}
|
||||
|
||||
; Flags live into loop.
|
||||
@@ -73,4 +73,4 @@ function %live_into_loop(i32) -> i32 {
|
||||
ebb1:
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op1jmpb#eb] jump ebb1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,13 @@ cd $(dirname "$0")
|
||||
topdir="$(pwd)"
|
||||
|
||||
# All the cretonne-* crates have the same version number
|
||||
# The filecheck crate version is managed independently.
|
||||
version="0.3.4"
|
||||
version="0.4.1"
|
||||
|
||||
# Update all of the Cargo.toml files.
|
||||
#
|
||||
# The main Cargo.toml in the top-level directory is the cretonne-tools crate which we don't publish.
|
||||
echo "Updating crate versions to $version"
|
||||
for crate in . lib/*; do
|
||||
if [ "$crate" = "lib/filecheck" ]; then
|
||||
continue
|
||||
fi
|
||||
# Update the version number of this crate to $version.
|
||||
sed -i.bk -e "s/^version = .*/version = \"$version\"/" "$crate/Cargo.toml"
|
||||
# Update the required version number of any cretonne* dependencies.
|
||||
@@ -31,7 +27,7 @@ cargo update
|
||||
|
||||
echo git commit -a -m "\"Bump version to $version"\"
|
||||
echo git push
|
||||
for crate in filecheck cretonne frontend native reader wasm; do
|
||||
for crate in cretonne frontend native reader wasm; do
|
||||
echo cargo publish --manifest-path "lib/$crate/Cargo.toml"
|
||||
done
|
||||
echo
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
//! The `cat` sub-command.
|
||||
//!
|
||||
//! Read a sequence of Cretonne IL files and print them again to stdout. This has the effect of
|
||||
//! Read a sequence of Cretonne IR files and print them again to stdout. This has the effect of
|
||||
//! normalizing formatting and removing comments.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use cretonne::ir::Function;
|
||||
use cton_reader::{parse_functions, TestCommand};
|
||||
use CommandResult;
|
||||
use cton_reader::parse_functions;
|
||||
use utils::read_to_string;
|
||||
use filetest::subtest::{self, SubTest, Context, Result as STResult};
|
||||
|
||||
pub fn run(files: Vec<String>) -> CommandResult {
|
||||
pub fn run(files: &[String]) -> CommandResult {
|
||||
for (i, f) in files.into_iter().enumerate() {
|
||||
if i != 0 {
|
||||
println!();
|
||||
@@ -20,7 +17,7 @@ pub fn run(files: Vec<String>) -> CommandResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cat_one(filename: String) -> CommandResult {
|
||||
fn cat_one(filename: &str) -> CommandResult {
|
||||
let buffer = read_to_string(&filename).map_err(
|
||||
|e| format!("{}: {}", filename, e),
|
||||
)?;
|
||||
@@ -37,34 +34,3 @@ fn cat_one(filename: String) -> CommandResult {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Object implementing the `test cat` sub-test.
|
||||
///
|
||||
/// This command is used for testing the parser and function printer. It simply parses a function
|
||||
/// and prints it out again.
|
||||
///
|
||||
/// The result is verified by filecheck.
|
||||
struct TestCat;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> STResult<Box<SubTest>> {
|
||||
assert_eq!(parsed.command, "cat");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestCat))
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestCat {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::from("cat")
|
||||
}
|
||||
|
||||
fn needs_verifier(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> STResult<()> {
|
||||
subtest::run_filecheck(&func.display(context.isa).to_string(), context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
//! CLI tool to compile cretonne IL into native code.
|
||||
//!
|
||||
//! Reads IR files into Cretonne IL and compiles it.
|
||||
//! CLI tool to read Cretonne IR files and compile them into native code.
|
||||
|
||||
use cton_reader::parse_test;
|
||||
use std::path::PathBuf;
|
||||
use cretonne::Context;
|
||||
use cretonne::print_errors::pretty_error;
|
||||
use cretonne::settings::FlagsOrIsa;
|
||||
use cretonne::{binemit, ir};
|
||||
use cton_reader::parse_test;
|
||||
use std::path::Path;
|
||||
use utils::{pretty_error, read_to_string, parse_sets_and_isa};
|
||||
use std::path::PathBuf;
|
||||
use utils::{parse_sets_and_isa, read_to_string};
|
||||
|
||||
struct PrintRelocs {
|
||||
flag_print: bool,
|
||||
@@ -45,26 +44,38 @@ impl binemit::RelocSink for PrintRelocs {
|
||||
}
|
||||
}
|
||||
|
||||
struct PrintTraps {
|
||||
flag_print: bool,
|
||||
}
|
||||
|
||||
impl binemit::TrapSink for PrintTraps {
|
||||
fn trap(&mut self, offset: binemit::CodeOffset, _srcloc: ir::SourceLoc, code: ir::TrapCode) {
|
||||
if self.flag_print {
|
||||
println!("trap: {} at {}", code, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
files: Vec<String>,
|
||||
flag_print: bool,
|
||||
flag_set: Vec<String>,
|
||||
flag_isa: String,
|
||||
flag_set: &[String],
|
||||
flag_isa: &str,
|
||||
) -> Result<(), String> {
|
||||
let parsed = parse_sets_and_isa(flag_set, flag_isa)?;
|
||||
|
||||
for filename in files {
|
||||
let path = Path::new(&filename);
|
||||
let name = String::from(path.as_os_str().to_string_lossy());
|
||||
handle_module(flag_print, path.to_path_buf(), name, parsed.as_fisa())?;
|
||||
handle_module(flag_print, &path.to_path_buf(), &name, parsed.as_fisa())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_module(
|
||||
flag_print: bool,
|
||||
path: PathBuf,
|
||||
name: String,
|
||||
path: &PathBuf,
|
||||
name: &str,
|
||||
fisa: FlagsOrIsa,
|
||||
) -> Result<(), String> {
|
||||
let buffer = read_to_string(&path).map_err(
|
||||
@@ -95,8 +106,9 @@ fn handle_module(
|
||||
// Encode the result as machine code.
|
||||
let mut mem = Vec::new();
|
||||
let mut relocs = PrintRelocs { flag_print };
|
||||
let mut traps = PrintTraps { flag_print };
|
||||
mem.resize(size as usize, 0);
|
||||
context.emit_to_memory(mem.as_mut_ptr(), &mut relocs, &*isa);
|
||||
context.emit_to_memory(mem.as_mut_ptr(), &mut relocs, &mut traps, &*isa);
|
||||
|
||||
if flag_print {
|
||||
print!(".byte ");
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
#[macro_use(dbg)]
|
||||
extern crate cretonne;
|
||||
extern crate cton_filetests;
|
||||
extern crate cton_reader;
|
||||
extern crate cton_wasm;
|
||||
extern crate docopt;
|
||||
extern crate filecheck;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate filecheck;
|
||||
extern crate num_cpus;
|
||||
extern crate tempdir;
|
||||
extern crate term;
|
||||
|
||||
use cretonne::{VERSION, timing};
|
||||
use cretonne::{timing, VERSION};
|
||||
use docopt::Docopt;
|
||||
use std::io::{self, Write};
|
||||
use std::process;
|
||||
|
||||
mod utils;
|
||||
mod filetest;
|
||||
mod cat;
|
||||
mod compile;
|
||||
mod print_cfg;
|
||||
mod rsfilecheck;
|
||||
mod utils;
|
||||
mod wasm;
|
||||
mod compile;
|
||||
|
||||
const USAGE: &str = "
|
||||
Cretonne code generator utility
|
||||
@@ -40,12 +38,12 @@ Options:
|
||||
-T, --time-passes
|
||||
print pass timing report
|
||||
-t, --just-decode
|
||||
just decode WebAssembly to Cretonne IL
|
||||
just decode WebAssembly to Cretonne IR
|
||||
-s, --print-size
|
||||
prints generated code size
|
||||
-c, --check-translation
|
||||
just checks the correctness of Cretonne IL translated from WebAssembly
|
||||
-p, --print print the resulting Cretonne IL
|
||||
just checks the correctness of Cretonne IR translated from WebAssembly
|
||||
-p, --print print the resulting Cretonne IR
|
||||
-h, --help print this help message
|
||||
--set=<set> configure Cretonne settings
|
||||
--isa=<isa> specify the Cretonne ISA
|
||||
@@ -88,15 +86,20 @@ fn cton_util() -> CommandResult {
|
||||
|
||||
// Find the sub-command to execute.
|
||||
let result = if args.cmd_test {
|
||||
filetest::run(args.flag_verbose, args.arg_file)
|
||||
cton_filetests::run(args.flag_verbose, &args.arg_file).map(|_time| ())
|
||||
} else if args.cmd_cat {
|
||||
cat::run(args.arg_file)
|
||||
cat::run(&args.arg_file)
|
||||
} else if args.cmd_filecheck {
|
||||
rsfilecheck::run(args.arg_file, args.flag_verbose)
|
||||
rsfilecheck::run(&args.arg_file, args.flag_verbose)
|
||||
} else if args.cmd_print_cfg {
|
||||
print_cfg::run(args.arg_file)
|
||||
print_cfg::run(&args.arg_file)
|
||||
} else if args.cmd_compile {
|
||||
compile::run(args.arg_file, args.flag_print, args.flag_set, args.flag_isa)
|
||||
compile::run(
|
||||
args.arg_file,
|
||||
args.flag_print,
|
||||
&args.flag_set,
|
||||
&args.flag_isa,
|
||||
)
|
||||
} else if args.cmd_wasm {
|
||||
wasm::run(
|
||||
args.arg_file,
|
||||
@@ -104,8 +107,8 @@ fn cton_util() -> CommandResult {
|
||||
args.flag_just_decode,
|
||||
args.flag_check_translation,
|
||||
args.flag_print,
|
||||
args.flag_set,
|
||||
args.flag_isa,
|
||||
&args.flag_set,
|
||||
&args.flag_isa,
|
||||
args.flag_print_size,
|
||||
)
|
||||
} else {
|
||||
|
||||
@@ -1,305 +0,0 @@
|
||||
//! Test command for testing the binary machine code emission.
|
||||
//!
|
||||
//! The `binemit` test command generates binary machine code for every instruction in the input
|
||||
//! functions and compares the results to the expected output.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
use cretonne::binemit;
|
||||
use cretonne::dbg::DisplayList;
|
||||
use cretonne::ir;
|
||||
use cretonne::ir::entities::AnyEntity;
|
||||
use cretonne::binemit::RegDiversions;
|
||||
use cton_reader::TestCommand;
|
||||
use filetest::subtest::{SubTest, Context, Result};
|
||||
use utils::{match_directive, pretty_error};
|
||||
|
||||
struct TestBinEmit;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
|
||||
assert_eq!(parsed.command, "binemit");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestBinEmit))
|
||||
}
|
||||
}
|
||||
|
||||
// Code sink that generates text.
|
||||
struct TextSink {
|
||||
offset: binemit::CodeOffset,
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl TextSink {
|
||||
/// Create a new empty TextSink.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
offset: 0,
|
||||
text: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl binemit::CodeSink for TextSink {
|
||||
fn offset(&self) -> binemit::CodeOffset {
|
||||
self.offset
|
||||
}
|
||||
|
||||
fn put1(&mut self, x: u8) {
|
||||
write!(self.text, "{:02x} ", x).unwrap();
|
||||
self.offset += 1;
|
||||
}
|
||||
|
||||
fn put2(&mut self, x: u16) {
|
||||
write!(self.text, "{:04x} ", x).unwrap();
|
||||
self.offset += 2;
|
||||
}
|
||||
|
||||
fn put4(&mut self, x: u32) {
|
||||
write!(self.text, "{:08x} ", x).unwrap();
|
||||
self.offset += 4;
|
||||
}
|
||||
|
||||
fn put8(&mut self, x: u64) {
|
||||
write!(self.text, "{:016x} ", x).unwrap();
|
||||
self.offset += 8;
|
||||
}
|
||||
|
||||
fn reloc_ebb(&mut self, reloc: binemit::Reloc, ebb_offset: binemit::CodeOffset) {
|
||||
write!(self.text, "{}({}) ", reloc, ebb_offset).unwrap();
|
||||
}
|
||||
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
reloc: binemit::Reloc,
|
||||
name: &ir::ExternalName,
|
||||
addend: binemit::Addend,
|
||||
) {
|
||||
write!(
|
||||
self.text,
|
||||
"{}({}",
|
||||
reloc,
|
||||
name,
|
||||
).unwrap();
|
||||
if addend != 0 {
|
||||
write!(
|
||||
self.text,
|
||||
"{:+}",
|
||||
addend,
|
||||
).unwrap();
|
||||
}
|
||||
write!(
|
||||
self.text,
|
||||
") ",
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
fn reloc_jt(&mut self, reloc: binemit::Reloc, jt: ir::JumpTable) {
|
||||
write!(self.text, "{}({}) ", reloc, jt).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestBinEmit {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::from("binemit")
|
||||
}
|
||||
|
||||
fn is_mutating(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn needs_isa(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()> {
|
||||
let isa = context.isa.expect("binemit needs an ISA");
|
||||
let encinfo = isa.encoding_info();
|
||||
// TODO: Run a verifier pass over the code first to detect any bad encodings or missing/bad
|
||||
// value locations. The current error reporting is just crashing...
|
||||
let mut func = func.into_owned();
|
||||
|
||||
// Fix the stack frame layout so we can test spill/fill encodings.
|
||||
let min_offset = func.stack_slots
|
||||
.keys()
|
||||
.map(|ss| func.stack_slots[ss].offset.unwrap())
|
||||
.min();
|
||||
func.stack_slots.frame_size = min_offset.map(|off| (-off) as u32);
|
||||
|
||||
let is_compressed = isa.flags().is_compressed();
|
||||
|
||||
// Give an encoding to any instruction that doesn't already have one.
|
||||
let mut divert = RegDiversions::new();
|
||||
for ebb in func.layout.ebbs() {
|
||||
divert.clear();
|
||||
for inst in func.layout.ebb_insts(ebb) {
|
||||
if !func.encodings[inst].is_legal() {
|
||||
// Find an encoding that satisfies both immediate field and register
|
||||
// constraints.
|
||||
if let Some(enc) = {
|
||||
let mut legal_encodings = isa.legal_encodings(
|
||||
&func.dfg,
|
||||
&func.dfg[inst],
|
||||
func.dfg.ctrl_typevar(inst),
|
||||
).filter(|e| {
|
||||
let recipe_constraints = &encinfo.constraints[e.recipe()];
|
||||
recipe_constraints.satisfied(inst, &divert, &func)
|
||||
});
|
||||
|
||||
if is_compressed {
|
||||
// Get the smallest legal encoding
|
||||
legal_encodings.min_by_key(|&e| encinfo.bytes(e))
|
||||
} else {
|
||||
// If not using compressed, just use the first encoding.
|
||||
legal_encodings.next()
|
||||
}
|
||||
}
|
||||
{
|
||||
func.encodings[inst] = enc;
|
||||
}
|
||||
}
|
||||
divert.apply(&func.dfg[inst]);
|
||||
}
|
||||
}
|
||||
|
||||
// Relax branches and compute EBB offsets based on the encodings.
|
||||
let code_size = binemit::relax_branches(&mut func, isa).map_err(|e| {
|
||||
pretty_error(&func, context.isa, e)
|
||||
})?;
|
||||
|
||||
// Collect all of the 'bin:' directives on instructions.
|
||||
let mut bins = HashMap::new();
|
||||
for comment in &context.details.comments {
|
||||
if let Some(want) = match_directive(comment.text, "bin:") {
|
||||
match comment.entity {
|
||||
AnyEntity::Inst(inst) => {
|
||||
if let Some(prev) = bins.insert(inst, want) {
|
||||
return Err(format!(
|
||||
"multiple 'bin:' directives on {}: '{}' and '{}'",
|
||||
func.dfg.display_inst(inst, isa),
|
||||
prev,
|
||||
want
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"'bin:' directive on non-inst {}: {}",
|
||||
comment.entity,
|
||||
comment.text
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if bins.is_empty() {
|
||||
return Err("No 'bin:' directives found".to_string());
|
||||
}
|
||||
|
||||
// Now emit all instructions.
|
||||
let mut sink = TextSink::new();
|
||||
for ebb in func.layout.ebbs() {
|
||||
divert.clear();
|
||||
// Correct header offsets should have been computed by `relax_branches()`.
|
||||
assert_eq!(
|
||||
sink.offset,
|
||||
func.offsets[ebb],
|
||||
"Inconsistent {} header offset",
|
||||
ebb
|
||||
);
|
||||
for (offset, inst, enc_bytes) in func.inst_offsets(ebb, &encinfo) {
|
||||
assert_eq!(sink.offset, offset);
|
||||
sink.text.clear();
|
||||
let enc = func.encodings[inst];
|
||||
|
||||
// Send legal encodings into the emitter.
|
||||
if enc.is_legal() {
|
||||
// Generate a better error message if output locations are not specified.
|
||||
if let Some(&v) = func.dfg.inst_results(inst).iter().find(|&&v| {
|
||||
!func.locations[v].is_assigned()
|
||||
})
|
||||
{
|
||||
return Err(format!(
|
||||
"Missing register/stack slot for {} in {}",
|
||||
v,
|
||||
func.dfg.display_inst(inst, isa)
|
||||
));
|
||||
}
|
||||
let before = sink.offset;
|
||||
isa.emit_inst(&func, inst, &mut divert, &mut sink);
|
||||
let emitted = sink.offset - before;
|
||||
// Verify the encoding recipe sizes against the ISAs emit_inst implementation.
|
||||
assert_eq!(
|
||||
emitted,
|
||||
enc_bytes,
|
||||
"Inconsistent size for [{}] {}",
|
||||
encinfo.display(enc),
|
||||
func.dfg.display_inst(inst, isa)
|
||||
);
|
||||
}
|
||||
|
||||
// Check against bin: directives.
|
||||
if let Some(want) = bins.remove(&inst) {
|
||||
if !enc.is_legal() {
|
||||
// A possible cause of an unencoded instruction is a missing location for
|
||||
// one of the input operands.
|
||||
if let Some(&v) = func.dfg.inst_args(inst).iter().find(|&&v| {
|
||||
!func.locations[v].is_assigned()
|
||||
})
|
||||
{
|
||||
return Err(format!(
|
||||
"Missing register/stack slot for {} in {}",
|
||||
v,
|
||||
func.dfg.display_inst(inst, isa)
|
||||
));
|
||||
}
|
||||
|
||||
// Do any encodings exist?
|
||||
let encodings = isa.legal_encodings(
|
||||
&func.dfg,
|
||||
&func.dfg[inst],
|
||||
func.dfg.ctrl_typevar(inst),
|
||||
).map(|e| encinfo.display(e))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if encodings.is_empty() {
|
||||
return Err(format!(
|
||||
"No encodings found for: {}",
|
||||
func.dfg.display_inst(inst, isa)
|
||||
));
|
||||
}
|
||||
return Err(format!(
|
||||
"No matching encodings for {} in {}",
|
||||
func.dfg.display_inst(inst, isa),
|
||||
DisplayList(&encodings),
|
||||
));
|
||||
}
|
||||
let have = sink.text.trim();
|
||||
if have != want {
|
||||
return Err(format!(
|
||||
"Bad machine code for {}: {}\nWant: {}\nGot: {}",
|
||||
inst,
|
||||
func.dfg.display_inst(inst, isa),
|
||||
want,
|
||||
have
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sink.offset != code_size {
|
||||
return Err(format!(
|
||||
"Expected code size {}, got {}",
|
||||
code_size,
|
||||
sink.offset
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
//! Test command for testing the code generator pipeline
|
||||
//!
|
||||
//! The `compile` test command runs each function through the full code generator pipeline
|
||||
|
||||
use cretonne::binemit;
|
||||
use cretonne::ir;
|
||||
use cretonne;
|
||||
use cton_reader::TestCommand;
|
||||
use filetest::subtest::{SubTest, Context, Result, run_filecheck};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
use utils::pretty_error;
|
||||
|
||||
struct TestCompile;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
|
||||
assert_eq!(parsed.command, "compile");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestCompile))
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestCompile {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::from("compile")
|
||||
}
|
||||
|
||||
fn is_mutating(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn needs_isa(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()> {
|
||||
let isa = context.isa.expect("compile needs an ISA");
|
||||
|
||||
// Create a compilation context, and drop in the function.
|
||||
let mut comp_ctx = cretonne::Context::new();
|
||||
comp_ctx.func = func.into_owned();
|
||||
|
||||
let code_size = comp_ctx.compile(isa).map_err(|e| {
|
||||
pretty_error(&comp_ctx.func, context.isa, e)
|
||||
})?;
|
||||
|
||||
dbg!(
|
||||
"Generated {} bytes of code:\n{}",
|
||||
code_size,
|
||||
comp_ctx.func.display(isa)
|
||||
);
|
||||
|
||||
// Verify that the returned code size matches the emitted bytes.
|
||||
let mut sink = SizeSink { offset: 0 };
|
||||
binemit::emit_function(
|
||||
&comp_ctx.func,
|
||||
|func, inst, div, sink| isa.emit_inst(func, inst, div, sink),
|
||||
&mut sink,
|
||||
);
|
||||
|
||||
if sink.offset != code_size {
|
||||
return Err(format!(
|
||||
"Expected code size {}, got {}",
|
||||
code_size,
|
||||
sink.offset
|
||||
));
|
||||
}
|
||||
|
||||
// Run final code through filecheck.
|
||||
let mut text = String::new();
|
||||
write!(&mut text, "{}", &comp_ctx.func.display(Some(isa)))
|
||||
.map_err(|e| e.to_string())?;
|
||||
run_filecheck(&text, context)
|
||||
}
|
||||
}
|
||||
|
||||
// Code sink that simply counts bytes.
|
||||
struct SizeSink {
|
||||
offset: binemit::CodeOffset,
|
||||
}
|
||||
|
||||
impl binemit::CodeSink for SizeSink {
|
||||
fn offset(&self) -> binemit::CodeOffset {
|
||||
self.offset
|
||||
}
|
||||
|
||||
fn put1(&mut self, _: u8) {
|
||||
self.offset += 1;
|
||||
}
|
||||
|
||||
fn put2(&mut self, _: u16) {
|
||||
self.offset += 2;
|
||||
}
|
||||
|
||||
fn put4(&mut self, _: u32) {
|
||||
self.offset += 4;
|
||||
}
|
||||
|
||||
fn put8(&mut self, _: u64) {
|
||||
self.offset += 8;
|
||||
}
|
||||
|
||||
fn reloc_ebb(&mut self, _reloc: binemit::Reloc, _ebb_offset: binemit::CodeOffset) {}
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
_reloc: binemit::Reloc,
|
||||
_name: &ir::ExternalName,
|
||||
_addend: binemit::Addend,
|
||||
) {
|
||||
}
|
||||
fn reloc_jt(&mut self, _reloc: binemit::Reloc, _jt: ir::JumpTable) {}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
//! Run tests concurrently.
|
||||
//!
|
||||
//! This module provides the `ConcurrentRunner` struct which uses a pool of threads to run tests
|
||||
//! concurrently.
|
||||
|
||||
use cretonne::timing;
|
||||
use std::panic::catch_unwind;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::mpsc::{channel, Sender, Receiver};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use num_cpus;
|
||||
use filetest::{TestResult, runone};
|
||||
|
||||
// Request sent to worker threads contains jobid and path.
|
||||
struct Request(usize, PathBuf);
|
||||
|
||||
/// Reply from worker thread,
|
||||
pub enum Reply {
|
||||
Starting { jobid: usize, thread_num: usize },
|
||||
Done { jobid: usize, result: TestResult },
|
||||
Tick,
|
||||
}
|
||||
|
||||
/// Manage threads that run test jobs concurrently.
|
||||
pub struct ConcurrentRunner {
|
||||
// Channel for sending requests to the worker threads.
|
||||
// The workers are sharing the receiver with an `Arc<Mutex<Receiver>>`.
|
||||
// This is `None` when shutting down.
|
||||
request_tx: Option<Sender<Request>>,
|
||||
|
||||
// Channel for receiving replies from the workers.
|
||||
// Workers have their own `Sender`.
|
||||
reply_rx: Receiver<Reply>,
|
||||
|
||||
handles: Vec<thread::JoinHandle<timing::PassTimes>>,
|
||||
}
|
||||
|
||||
impl ConcurrentRunner {
|
||||
/// Create a new `ConcurrentRunner` with threads spun up.
|
||||
pub fn new() -> Self {
|
||||
let (request_tx, request_rx) = channel();
|
||||
let request_mutex = Arc::new(Mutex::new(request_rx));
|
||||
let (reply_tx, reply_rx) = channel();
|
||||
|
||||
heartbeat_thread(reply_tx.clone());
|
||||
|
||||
let handles = (0..num_cpus::get())
|
||||
.map(|num| {
|
||||
worker_thread(num, request_mutex.clone(), reply_tx.clone())
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
request_tx: Some(request_tx),
|
||||
reply_rx,
|
||||
handles,
|
||||
}
|
||||
}
|
||||
|
||||
/// Shut down worker threads orderly. They will finish any queued jobs first.
|
||||
pub fn shutdown(&mut self) {
|
||||
self.request_tx = None;
|
||||
}
|
||||
|
||||
/// Join all the worker threads.
|
||||
/// Transfer pass timings from the worker threads to the current thread.
|
||||
pub fn join(&mut self) {
|
||||
assert!(self.request_tx.is_none(), "must shutdown before join");
|
||||
for h in self.handles.drain(..) {
|
||||
match h.join() {
|
||||
Ok(t) => timing::add_to_current(t),
|
||||
Err(e) => println!("worker panicked: {:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a new job to the queues.
|
||||
pub fn put(&mut self, jobid: usize, path: &Path) {
|
||||
self.request_tx
|
||||
.as_ref()
|
||||
.expect("cannot push after shutdown")
|
||||
.send(Request(jobid, path.to_owned()))
|
||||
.expect("all the worker threads are gone");
|
||||
}
|
||||
|
||||
/// Get a job reply without blocking.
|
||||
pub fn try_get(&mut self) -> Option<Reply> {
|
||||
self.reply_rx.try_recv().ok()
|
||||
}
|
||||
|
||||
/// Get a job reply, blocking until one is available.
|
||||
pub fn get(&mut self) -> Option<Reply> {
|
||||
self.reply_rx.recv().ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a heartbeat thread which sends ticks down the reply channel every second.
|
||||
/// This lets us implement timeouts without the not yet stable `recv_timeout`.
|
||||
fn heartbeat_thread(replies: Sender<Reply>) -> thread::JoinHandle<()> {
|
||||
thread::Builder::new()
|
||||
.name("heartbeat".to_string())
|
||||
.spawn(move || while replies.send(Reply::Tick).is_ok() {
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Spawn a worker thread running tests.
|
||||
fn worker_thread(
|
||||
thread_num: usize,
|
||||
requests: Arc<Mutex<Receiver<Request>>>,
|
||||
replies: Sender<Reply>,
|
||||
) -> thread::JoinHandle<timing::PassTimes> {
|
||||
thread::Builder::new()
|
||||
.name(format!("worker #{}", thread_num))
|
||||
.spawn(move || {
|
||||
loop {
|
||||
// Lock the mutex only long enough to extract a request.
|
||||
let Request(jobid, path) = match requests.lock().unwrap().recv() {
|
||||
Err(..) => break, // TX end shut down. exit thread.
|
||||
Ok(req) => req,
|
||||
};
|
||||
|
||||
// Tell them we're starting this job.
|
||||
// The receiver should always be present for this as long as we have jobs.
|
||||
replies.send(Reply::Starting { jobid, thread_num }).unwrap();
|
||||
|
||||
let result = catch_unwind(|| runone::run(path.as_path())).unwrap_or_else(|e| {
|
||||
// The test panicked, leaving us a `Box<Any>`.
|
||||
// Panics are usually strings.
|
||||
if let Some(msg) = e.downcast_ref::<String>() {
|
||||
Err(format!("panicked in worker #{}: {}", thread_num, msg))
|
||||
} else if let Some(msg) = e.downcast_ref::<&'static str>() {
|
||||
Err(format!("panicked in worker #{}: {}", thread_num, msg))
|
||||
} else {
|
||||
Err(format!("panicked in worker #{}", thread_num))
|
||||
}
|
||||
});
|
||||
|
||||
if let Err(ref msg) = result {
|
||||
dbg!("FAIL: {}", msg);
|
||||
}
|
||||
|
||||
replies.send(Reply::Done { jobid, result }).unwrap();
|
||||
}
|
||||
|
||||
// Timing is accumulated independently per thread.
|
||||
// Timings from this worker thread will be aggregated by `ConcurrentRunner::join()`.
|
||||
timing::take_current()
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
//! Test command for verifying dominator trees.
|
||||
//!
|
||||
//! The `test domtree` test command looks for annotations on instructions like this:
|
||||
//!
|
||||
//! jump ebb3 ; dominates: ebb3
|
||||
//!
|
||||
//! This annotation means that the jump instruction is expected to be the immediate dominator of
|
||||
//! `ebb3`.
|
||||
//!
|
||||
//! We verify that the dominator tree annotations are complete and correct.
|
||||
//!
|
||||
|
||||
use cretonne::dominator_tree::{DominatorTree, DominatorTreePreorder};
|
||||
use cretonne::flowgraph::ControlFlowGraph;
|
||||
use cretonne::ir::Function;
|
||||
use cretonne::ir::entities::AnyEntity;
|
||||
use cton_reader::TestCommand;
|
||||
use filetest::subtest::{SubTest, Context, Result, run_filecheck};
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{self, Write};
|
||||
use std::result;
|
||||
use utils::match_directive;
|
||||
|
||||
struct TestDomtree;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
|
||||
assert_eq!(parsed.command, "domtree");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestDomtree))
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestDomtree {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::from("domtree")
|
||||
}
|
||||
|
||||
// Extract our own dominator tree from
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> Result<()> {
|
||||
let func = func.borrow();
|
||||
let cfg = ControlFlowGraph::with_function(func);
|
||||
let domtree = DominatorTree::with_function(func, &cfg);
|
||||
|
||||
// Build an expected domtree from the source annotations.
|
||||
let mut expected = HashMap::new();
|
||||
for comment in &context.details.comments {
|
||||
if let Some(tail) = match_directive(comment.text, "dominates:") {
|
||||
let inst = match comment.entity {
|
||||
AnyEntity::Inst(inst) => inst,
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"annotation on non-inst {}: {}",
|
||||
comment.entity,
|
||||
comment.text
|
||||
))
|
||||
}
|
||||
};
|
||||
for src_ebb in tail.split_whitespace() {
|
||||
let ebb = match context.details.map.lookup_str(src_ebb) {
|
||||
Some(AnyEntity::Ebb(ebb)) => ebb,
|
||||
_ => return Err(format!("expected defined EBB, got {}", src_ebb)),
|
||||
};
|
||||
|
||||
// Annotations say that `inst` is the idom of `ebb`.
|
||||
if expected.insert(ebb, inst).is_some() {
|
||||
return Err(format!("multiple dominators for {}", src_ebb));
|
||||
}
|
||||
|
||||
// Compare to computed domtree.
|
||||
match domtree.idom(ebb) {
|
||||
Some(got_inst) if got_inst != inst => {
|
||||
return Err(format!(
|
||||
"mismatching idoms for {}:\n\
|
||||
want: {}, got: {}",
|
||||
src_ebb,
|
||||
inst,
|
||||
got_inst
|
||||
));
|
||||
}
|
||||
None => {
|
||||
return Err(format!(
|
||||
"mismatching idoms for {}:\n\
|
||||
want: {}, got: unreachable",
|
||||
src_ebb,
|
||||
inst
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we know that everything in `expected` is consistent with `domtree`.
|
||||
// All other EBB's should be either unreachable or the entry block.
|
||||
for ebb in func.layout.ebbs().skip(1).filter(
|
||||
|ebb| !expected.contains_key(ebb),
|
||||
)
|
||||
{
|
||||
if let Some(got_inst) = domtree.idom(ebb) {
|
||||
return Err(format!(
|
||||
"mismatching idoms for renumbered {}:\n\
|
||||
want: unrechable, got: {}",
|
||||
ebb,
|
||||
got_inst
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let text = filecheck_text(func, &domtree).expect("formatting error");
|
||||
run_filecheck(&text, context)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate some output for filecheck testing
|
||||
fn filecheck_text(func: &Function, domtree: &DominatorTree) -> result::Result<String, fmt::Error> {
|
||||
let mut s = String::new();
|
||||
|
||||
write!(s, "cfg_postorder:")?;
|
||||
for &ebb in domtree.cfg_postorder() {
|
||||
write!(s, " {}", ebb)?;
|
||||
}
|
||||
writeln!(s, "")?;
|
||||
|
||||
// Compute and print out a pre-order of the dominator tree.
|
||||
writeln!(s, "domtree_preorder {{")?;
|
||||
let mut dtpo = DominatorTreePreorder::new();
|
||||
dtpo.compute(domtree, &func.layout);
|
||||
let mut stack = Vec::new();
|
||||
stack.extend(func.layout.entry_block());
|
||||
while let Some(ebb) = stack.pop() {
|
||||
write!(s, " {}:", ebb)?;
|
||||
let i = stack.len();
|
||||
for ch in dtpo.children(ebb) {
|
||||
write!(s, " {}", ch)?;
|
||||
stack.push(ch);
|
||||
}
|
||||
writeln!(s, "")?;
|
||||
// Reverse the children we just pushed so we'll pop them in order.
|
||||
stack[i..].reverse();
|
||||
}
|
||||
writeln!(s, "}}")?;
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
//! Test command for checking the IL legalizer.
|
||||
//!
|
||||
//! The `test legalizer` test command runs each function through `legalize_function()` and sends
|
||||
//! the result to filecheck.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use cretonne;
|
||||
use cretonne::ir::Function;
|
||||
use cton_reader::TestCommand;
|
||||
use filetest::subtest::{SubTest, Context, Result, run_filecheck};
|
||||
use std::fmt::Write;
|
||||
use utils::pretty_error;
|
||||
|
||||
struct TestLegalizer;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
|
||||
assert_eq!(parsed.command, "legalizer");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestLegalizer))
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestLegalizer {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::from("legalizer")
|
||||
}
|
||||
|
||||
fn is_mutating(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn needs_isa(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> Result<()> {
|
||||
let mut comp_ctx = cretonne::Context::new();
|
||||
comp_ctx.func = func.into_owned();
|
||||
let isa = context.isa.expect("legalizer needs an ISA");
|
||||
|
||||
comp_ctx.compute_cfg();
|
||||
comp_ctx.legalize(isa).map_err(|e| {
|
||||
pretty_error(&comp_ctx.func, context.isa, e)
|
||||
})?;
|
||||
|
||||
let mut text = String::new();
|
||||
write!(&mut text, "{}", &comp_ctx.func.display(Some(isa)))
|
||||
.map_err(|e| e.to_string())?;
|
||||
run_filecheck(&text, context)
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
//! Test command for testing the LICM pass.
|
||||
//!
|
||||
//! The `licm` test command runs each function through the LICM pass after ensuring
|
||||
//! that all instructions are legal for the target.
|
||||
//!
|
||||
//! The resulting function is sent to `filecheck`.
|
||||
|
||||
use cretonne::ir::Function;
|
||||
use cretonne;
|
||||
use cton_reader::TestCommand;
|
||||
use filetest::subtest::{SubTest, Context, Result, run_filecheck};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
use utils::pretty_error;
|
||||
|
||||
struct TestLICM;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
|
||||
assert_eq!(parsed.command, "licm");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestLICM))
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestLICM {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::from("licm")
|
||||
}
|
||||
|
||||
fn is_mutating(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> Result<()> {
|
||||
// Create a compilation context, and drop in the function.
|
||||
let mut comp_ctx = cretonne::Context::new();
|
||||
comp_ctx.func = func.into_owned();
|
||||
|
||||
comp_ctx.flowgraph();
|
||||
comp_ctx.compute_loop_analysis();
|
||||
comp_ctx.licm(context.flags_or_isa()).map_err(|e| {
|
||||
pretty_error(&comp_ctx.func, context.isa, Into::into(e))
|
||||
})?;
|
||||
|
||||
let mut text = String::new();
|
||||
write!(&mut text, "{}", &comp_ctx.func).map_err(
|
||||
|e| e.to_string(),
|
||||
)?;
|
||||
run_filecheck(&text, context)
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
//! File tests.
|
||||
//!
|
||||
//! This module contains the main driver for `cton-util test` as well as implementations of the
|
||||
//! available test commands.
|
||||
|
||||
use std::path::Path;
|
||||
use std::time;
|
||||
use cton_reader::TestCommand;
|
||||
use CommandResult;
|
||||
use cat;
|
||||
use print_cfg;
|
||||
use filetest::runner::TestRunner;
|
||||
|
||||
pub mod subtest;
|
||||
|
||||
mod binemit;
|
||||
mod compile;
|
||||
mod concurrent;
|
||||
mod domtree;
|
||||
mod legalizer;
|
||||
mod licm;
|
||||
mod preopt;
|
||||
mod regalloc;
|
||||
mod runner;
|
||||
mod runone;
|
||||
mod simple_gvn;
|
||||
mod verifier;
|
||||
|
||||
/// The result of running the test in a file.
|
||||
pub type TestResult = Result<time::Duration, String>;
|
||||
|
||||
/// Main entry point for `cton-util test`.
|
||||
///
|
||||
/// Take a list of filenames which can be either `.cton` files or directories.
|
||||
///
|
||||
/// Files are interpreted as test cases and executed immediately.
|
||||
///
|
||||
/// Directories are scanned recursively for test cases ending in `.cton`. These test cases are
|
||||
/// executed on background threads.
|
||||
///
|
||||
pub fn run(verbose: bool, files: Vec<String>) -> CommandResult {
|
||||
let mut runner = TestRunner::new(verbose);
|
||||
|
||||
for path in files.iter().map(Path::new) {
|
||||
if path.is_file() {
|
||||
runner.push_test(path);
|
||||
} else {
|
||||
runner.push_dir(path);
|
||||
}
|
||||
}
|
||||
|
||||
runner.start_threads();
|
||||
runner.run()
|
||||
}
|
||||
|
||||
/// Create a new subcommand trait object to match `parsed.command`.
|
||||
///
|
||||
/// This function knows how to create all of the possible `test <foo>` commands that can appear in
|
||||
/// a `.cton` test file.
|
||||
fn new_subtest(parsed: &TestCommand) -> subtest::Result<Box<subtest::SubTest>> {
|
||||
match parsed.command {
|
||||
"binemit" => binemit::subtest(parsed),
|
||||
"cat" => cat::subtest(parsed),
|
||||
"compile" => compile::subtest(parsed),
|
||||
"domtree" => domtree::subtest(parsed),
|
||||
"legalizer" => legalizer::subtest(parsed),
|
||||
"licm" => licm::subtest(parsed),
|
||||
"preopt" => preopt::subtest(parsed),
|
||||
"print-cfg" => print_cfg::subtest(parsed),
|
||||
"regalloc" => regalloc::subtest(parsed),
|
||||
"simple-gvn" => simple_gvn::subtest(parsed),
|
||||
"verifier" => verifier::subtest(parsed),
|
||||
_ => Err(format!("unknown test command '{}'", parsed.command)),
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
//! Test command for testing the preopt pass.
|
||||
//!
|
||||
//! The resulting function is sent to `filecheck`.
|
||||
|
||||
use cretonne::ir::Function;
|
||||
use cretonne;
|
||||
use cton_reader::TestCommand;
|
||||
use filetest::subtest::{SubTest, Context, Result, run_filecheck};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
use utils::pretty_error;
|
||||
|
||||
struct TestPreopt;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
|
||||
assert_eq!(parsed.command, "preopt");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestPreopt))
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestPreopt {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::from("preopt")
|
||||
}
|
||||
|
||||
fn is_mutating(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> Result<()> {
|
||||
// Create a compilation context, and drop in the function.
|
||||
let mut comp_ctx = cretonne::Context::new();
|
||||
comp_ctx.func = func.into_owned();
|
||||
let isa = context.isa.expect("preopt needs an ISA");
|
||||
|
||||
comp_ctx.flowgraph();
|
||||
comp_ctx.preopt(isa).map_err(|e| {
|
||||
pretty_error(&comp_ctx.func, context.isa, Into::into(e))
|
||||
})?;
|
||||
|
||||
let mut text = String::new();
|
||||
write!(&mut text, "{}", &comp_ctx.func).map_err(
|
||||
|e| e.to_string(),
|
||||
)?;
|
||||
run_filecheck(&text, context)
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
//! Test command for testing the register allocator.
|
||||
//!
|
||||
//! The `regalloc` test command runs each function through the register allocator after ensuring
|
||||
//! that all instructions are legal for the target.
|
||||
//!
|
||||
//! The resulting function is sent to `filecheck`.
|
||||
|
||||
use cretonne::ir::Function;
|
||||
use cretonne;
|
||||
use cton_reader::TestCommand;
|
||||
use filetest::subtest::{SubTest, Context, Result, run_filecheck};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
use utils::pretty_error;
|
||||
|
||||
struct TestRegalloc;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
|
||||
assert_eq!(parsed.command, "regalloc");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestRegalloc))
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestRegalloc {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::from("regalloc")
|
||||
}
|
||||
|
||||
fn is_mutating(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn needs_isa(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> Result<()> {
|
||||
let isa = context.isa.expect("register allocator needs an ISA");
|
||||
|
||||
// Create a compilation context, and drop in the function.
|
||||
let mut comp_ctx = cretonne::Context::new();
|
||||
comp_ctx.func = func.into_owned();
|
||||
|
||||
comp_ctx.compute_cfg();
|
||||
// TODO: Should we have an option to skip legalization?
|
||||
comp_ctx.legalize(isa).map_err(|e| {
|
||||
pretty_error(&comp_ctx.func, context.isa, e)
|
||||
})?;
|
||||
comp_ctx.compute_domtree();
|
||||
comp_ctx.regalloc(isa).map_err(|e| {
|
||||
pretty_error(&comp_ctx.func, context.isa, e)
|
||||
})?;
|
||||
|
||||
let mut text = String::new();
|
||||
write!(&mut text, "{}", &comp_ctx.func.display(Some(isa)))
|
||||
.map_err(|e| e.to_string())?;
|
||||
run_filecheck(&text, context)
|
||||
}
|
||||
}
|
||||
@@ -1,338 +0,0 @@
|
||||
//! Test runner.
|
||||
//!
|
||||
//! This module implements the `TestRunner` struct which manages executing tests as well as
|
||||
//! scanning directories for tests.
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt::{self, Display};
|
||||
use std::ffi::OsStr;
|
||||
use std::path::{Path, PathBuf};
|
||||
use filetest::{TestResult, runone};
|
||||
use filetest::concurrent::{ConcurrentRunner, Reply};
|
||||
use CommandResult;
|
||||
|
||||
// Timeout in seconds when we're not making progress.
|
||||
const TIMEOUT_PANIC: usize = 10;
|
||||
|
||||
// Timeout for reporting slow tests without panicking.
|
||||
const TIMEOUT_SLOW: usize = 3;
|
||||
|
||||
struct QueueEntry {
|
||||
path: PathBuf,
|
||||
state: State,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
enum State {
|
||||
New,
|
||||
Queued,
|
||||
Running,
|
||||
Done(TestResult),
|
||||
}
|
||||
|
||||
impl QueueEntry {
|
||||
pub fn path(&self) -> &Path {
|
||||
self.path.as_path()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for QueueEntry {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let p = self.path.to_string_lossy();
|
||||
match self.state {
|
||||
State::Done(Ok(dur)) => {
|
||||
write!(
|
||||
f,
|
||||
"{}.{:03} {}",
|
||||
dur.as_secs(),
|
||||
dur.subsec_nanos() / 1000000,
|
||||
p
|
||||
)
|
||||
}
|
||||
State::Done(Err(ref e)) => write!(f, "FAIL {}: {}", p, e),
|
||||
_ => write!(f, "{}", p),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestRunner {
|
||||
verbose: bool,
|
||||
|
||||
// Directories that have not yet been scanned.
|
||||
dir_stack: Vec<PathBuf>,
|
||||
|
||||
// Filenames of tests to run.
|
||||
tests: Vec<QueueEntry>,
|
||||
|
||||
// Pointer into `tests` where the `New` entries begin.
|
||||
new_tests: usize,
|
||||
|
||||
// Number of contiguous reported tests at the front of `tests`.
|
||||
reported_tests: usize,
|
||||
|
||||
// Number of errors seen so far.
|
||||
errors: usize,
|
||||
|
||||
// Number of ticks received since we saw any progress.
|
||||
ticks_since_progress: usize,
|
||||
|
||||
threads: Option<ConcurrentRunner>,
|
||||
}
|
||||
|
||||
impl TestRunner {
|
||||
/// Create a new blank TrstRunner.
|
||||
pub fn new(verbose: bool) -> Self {
|
||||
Self {
|
||||
verbose,
|
||||
dir_stack: Vec::new(),
|
||||
tests: Vec::new(),
|
||||
new_tests: 0,
|
||||
reported_tests: 0,
|
||||
errors: 0,
|
||||
ticks_since_progress: 0,
|
||||
threads: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a directory path to be scanned later.
|
||||
///
|
||||
/// If `dir` turns out to be a regular file, it is silently ignored.
|
||||
/// Otherwise, any problems reading the directory are reported.
|
||||
pub fn push_dir<P: Into<PathBuf>>(&mut self, dir: P) {
|
||||
self.dir_stack.push(dir.into());
|
||||
}
|
||||
|
||||
/// Add a test to be executed later.
|
||||
///
|
||||
/// Any problems reading `file` as a test case file will be reported as a test failure.
|
||||
pub fn push_test<P: Into<PathBuf>>(&mut self, file: P) {
|
||||
self.tests.push(QueueEntry {
|
||||
path: file.into(),
|
||||
state: State::New,
|
||||
});
|
||||
}
|
||||
|
||||
/// Begin running tests concurrently.
|
||||
pub fn start_threads(&mut self) {
|
||||
assert!(self.threads.is_none());
|
||||
self.threads = Some(ConcurrentRunner::new());
|
||||
}
|
||||
|
||||
/// Scan any directories pushed so far.
|
||||
/// Push any potential test cases found.
|
||||
pub fn scan_dirs(&mut self) {
|
||||
// This recursive search tries to minimize statting in a directory hierarchy containing
|
||||
// mostly test cases.
|
||||
//
|
||||
// - Directory entries with a "cton" extension are presumed to be test case files.
|
||||
// - Directory entries with no extension are presumed to be subdirectories.
|
||||
// - Anything else is ignored.
|
||||
//
|
||||
while let Some(dir) = self.dir_stack.pop() {
|
||||
match dir.read_dir() {
|
||||
Err(err) => {
|
||||
// Fail silently if `dir` was actually a regular file.
|
||||
// This lets us skip spurious extensionless files without statting everything
|
||||
// needlessly.
|
||||
if !dir.is_file() {
|
||||
self.path_error(dir, err);
|
||||
}
|
||||
}
|
||||
Ok(entries) => {
|
||||
// Read all directory entries. Avoid statting.
|
||||
for entry_result in entries {
|
||||
match entry_result {
|
||||
Err(err) => {
|
||||
// Not sure why this would happen. `read_dir` succeeds, but there's
|
||||
// a problem with an entry. I/O error during a getdirentries
|
||||
// syscall seems to be the reason. The implementation in
|
||||
// libstd/sys/unix/fs.rs seems to suggest that breaking now would
|
||||
// be a good idea, or the iterator could keep returning the same
|
||||
// error forever.
|
||||
self.path_error(dir, err);
|
||||
break;
|
||||
}
|
||||
Ok(entry) => {
|
||||
let path = entry.path();
|
||||
// Recognize directories and tests by extension.
|
||||
// Yes, this means we ignore directories with '.' in their name.
|
||||
match path.extension().and_then(OsStr::to_str) {
|
||||
Some("cton") => self.push_test(path),
|
||||
Some(_) => {}
|
||||
None => self.push_dir(path),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get the new jobs running before moving on to the next directory.
|
||||
self.schedule_jobs();
|
||||
}
|
||||
}
|
||||
|
||||
/// Report an error related to a path.
|
||||
fn path_error<E: Error>(&mut self, path: PathBuf, err: E) {
|
||||
self.errors += 1;
|
||||
println!("{}: {}", path.to_string_lossy(), err);
|
||||
}
|
||||
|
||||
/// Report on the next in-order job, if it's done.
|
||||
fn report_job(&self) -> bool {
|
||||
let jobid = self.reported_tests;
|
||||
if let Some(&QueueEntry { state: State::Done(ref result), .. }) = self.tests.get(jobid) {
|
||||
if self.verbose || result.is_err() {
|
||||
println!("{}", self.tests[jobid]);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedule any new jobs to run.
|
||||
fn schedule_jobs(&mut self) {
|
||||
for jobid in self.new_tests..self.tests.len() {
|
||||
assert_eq!(self.tests[jobid].state, State::New);
|
||||
if let Some(ref mut conc) = self.threads {
|
||||
// Queue test for concurrent execution.
|
||||
self.tests[jobid].state = State::Queued;
|
||||
conc.put(jobid, self.tests[jobid].path());
|
||||
} else {
|
||||
// Run test synchronously.
|
||||
self.tests[jobid].state = State::Running;
|
||||
let result = runone::run(self.tests[jobid].path());
|
||||
self.finish_job(jobid, result);
|
||||
}
|
||||
self.new_tests = jobid + 1;
|
||||
}
|
||||
|
||||
// Check for any asynchronous replies without blocking.
|
||||
while let Some(reply) = self.threads.as_mut().and_then(ConcurrentRunner::try_get) {
|
||||
self.handle_reply(reply);
|
||||
}
|
||||
}
|
||||
|
||||
/// Report the end of a job.
|
||||
fn finish_job(&mut self, jobid: usize, result: TestResult) {
|
||||
assert_eq!(self.tests[jobid].state, State::Running);
|
||||
if result.is_err() {
|
||||
self.errors += 1;
|
||||
}
|
||||
self.tests[jobid].state = State::Done(result);
|
||||
|
||||
// Reports jobs in order.
|
||||
while self.report_job() {
|
||||
self.reported_tests += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle a reply from the async threads.
|
||||
fn handle_reply(&mut self, reply: Reply) {
|
||||
match reply {
|
||||
Reply::Starting { jobid, .. } => {
|
||||
assert_eq!(self.tests[jobid].state, State::Queued);
|
||||
self.tests[jobid].state = State::Running;
|
||||
}
|
||||
Reply::Done { jobid, result } => {
|
||||
self.ticks_since_progress = 0;
|
||||
self.finish_job(jobid, result)
|
||||
}
|
||||
Reply::Tick => {
|
||||
self.ticks_since_progress += 1;
|
||||
if self.ticks_since_progress == TIMEOUT_SLOW {
|
||||
println!(
|
||||
"STALLED for {} seconds with {}/{} tests finished",
|
||||
self.ticks_since_progress,
|
||||
self.reported_tests,
|
||||
self.tests.len()
|
||||
);
|
||||
for jobid in self.reported_tests..self.tests.len() {
|
||||
if self.tests[jobid].state == State::Running {
|
||||
println!("slow: {}", self.tests[jobid]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.ticks_since_progress >= TIMEOUT_PANIC {
|
||||
panic!(
|
||||
"worker threads stalled for {} seconds.",
|
||||
self.ticks_since_progress
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Drain the async jobs and shut down the threads.
|
||||
fn drain_threads(&mut self) {
|
||||
if let Some(mut conc) = self.threads.take() {
|
||||
conc.shutdown();
|
||||
while self.reported_tests < self.tests.len() {
|
||||
match conc.get() {
|
||||
Some(reply) => self.handle_reply(reply),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
conc.join();
|
||||
}
|
||||
}
|
||||
|
||||
/// Print out a report of slow tests.
|
||||
fn report_slow_tests(&self) {
|
||||
// Collect runtimes of succeeded tests.
|
||||
let mut times = self.tests
|
||||
.iter()
|
||||
.filter_map(|entry| match *entry {
|
||||
QueueEntry { state: State::Done(Ok(dur)), .. } => Some(dur),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Get me some real data, kid.
|
||||
let len = times.len();
|
||||
if len < 4 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute quartiles.
|
||||
times.sort();
|
||||
let qlen = len / 4;
|
||||
let q1 = times[qlen];
|
||||
let q3 = times[len - 1 - qlen];
|
||||
// Inter-quartile range.
|
||||
let iqr = q3 - q1;
|
||||
|
||||
|
||||
// Cut-off for what we consider a 'slow' test: 3 IQR from the 75% quartile.
|
||||
//
|
||||
// Q3 + 1.5 IQR are the data points that would be plotted as outliers outside a box plot,
|
||||
// but we have a wider distribution of test times, so double it to 3 IQR.
|
||||
let cut = q3 + iqr * 3;
|
||||
if cut > *times.last().unwrap() {
|
||||
return;
|
||||
}
|
||||
|
||||
for t in self.tests.iter().filter(|entry| match **entry {
|
||||
QueueEntry { state: State::Done(Ok(dur)), .. } => dur > cut,
|
||||
_ => false,
|
||||
})
|
||||
{
|
||||
println!("slow: {}", t)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Scan pushed directories for tests and run them.
|
||||
pub fn run(&mut self) -> CommandResult {
|
||||
self.scan_dirs();
|
||||
self.schedule_jobs();
|
||||
self.drain_threads();
|
||||
self.report_slow_tests();
|
||||
println!("{} tests", self.tests.len());
|
||||
match self.errors {
|
||||
0 => Ok(()),
|
||||
1 => Err("1 failure".to_string()),
|
||||
n => Err(format!("{} failures", n)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
//! Run the tests in a single test file.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::time;
|
||||
use cretonne::ir::Function;
|
||||
use cretonne::isa::TargetIsa;
|
||||
use cretonne::settings::Flags;
|
||||
use cretonne::timing;
|
||||
use cretonne::verify_function;
|
||||
use cton_reader::parse_test;
|
||||
use cton_reader::IsaSpec;
|
||||
use utils::{read_to_string, pretty_verifier_error};
|
||||
use filetest::{TestResult, new_subtest};
|
||||
use filetest::subtest::{SubTest, Context, Result};
|
||||
|
||||
/// Load `path` and run the test in it.
|
||||
///
|
||||
/// If running this test causes a panic, it will propagate as normal.
|
||||
pub fn run(path: &Path) -> TestResult {
|
||||
let _tt = timing::process_file();
|
||||
dbg!("---\nFile: {}", path.to_string_lossy());
|
||||
let started = time::Instant::now();
|
||||
let buffer = read_to_string(path).map_err(|e| e.to_string())?;
|
||||
let testfile = parse_test(&buffer).map_err(|e| e.to_string())?;
|
||||
if testfile.functions.is_empty() {
|
||||
return Err("no functions found".to_string());
|
||||
}
|
||||
|
||||
// Parse the test commands.
|
||||
let mut tests = testfile
|
||||
.commands
|
||||
.iter()
|
||||
.map(new_subtest)
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
// Flags to use for those tests that don't need an ISA.
|
||||
// This is the cumulative effect of all the `set` commands in the file.
|
||||
let flags = match testfile.isa_spec {
|
||||
IsaSpec::None(ref f) => f,
|
||||
IsaSpec::Some(ref v) => v.last().expect("Empty ISA list").flags(),
|
||||
};
|
||||
|
||||
// Sort the tests so the mutators are at the end, and those that don't need the verifier are at
|
||||
// the front.
|
||||
tests.sort_by_key(|st| (st.is_mutating(), st.needs_verifier()));
|
||||
|
||||
// Expand the tests into (test, flags, isa) tuples.
|
||||
let mut tuples = test_tuples(&tests, &testfile.isa_spec, flags)?;
|
||||
|
||||
// Isolate the last test in the hope that this is the only mutating test.
|
||||
// If so, we can completely avoid cloning functions.
|
||||
let last_tuple = match tuples.pop() {
|
||||
None => return Err("no test commands found".to_string()),
|
||||
Some(t) => t,
|
||||
};
|
||||
|
||||
for (func, details) in testfile.functions {
|
||||
let mut context = Context {
|
||||
preamble_comments: &testfile.preamble_comments,
|
||||
details,
|
||||
verified: false,
|
||||
flags,
|
||||
isa: None,
|
||||
};
|
||||
|
||||
for tuple in &tuples {
|
||||
run_one_test(*tuple, Cow::Borrowed(&func), &mut context)?;
|
||||
}
|
||||
// Run the last test with an owned function which means it won't need to clone it before
|
||||
// mutating.
|
||||
run_one_test(last_tuple, Cow::Owned(func), &mut context)?;
|
||||
}
|
||||
|
||||
|
||||
Ok(started.elapsed())
|
||||
}
|
||||
|
||||
// Given a slice of tests, generate a vector of (test, flags, isa) tuples.
|
||||
fn test_tuples<'a>(
|
||||
tests: &'a [Box<SubTest>],
|
||||
isa_spec: &'a IsaSpec,
|
||||
no_isa_flags: &'a Flags,
|
||||
) -> Result<Vec<(&'a SubTest, &'a Flags, Option<&'a TargetIsa>)>> {
|
||||
let mut out = Vec::new();
|
||||
for test in tests {
|
||||
if test.needs_isa() {
|
||||
match *isa_spec {
|
||||
IsaSpec::None(_) => {
|
||||
// TODO: Generate a list of default ISAs.
|
||||
return Err(format!("test {} requires an ISA", test.name()));
|
||||
}
|
||||
IsaSpec::Some(ref isas) => {
|
||||
for isa in isas {
|
||||
out.push((&**test, isa.flags(), Some(&**isa)));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This test doesn't require an ISA, and we only want to run one instance of it.
|
||||
// Still, give it an ISA ref if we happen to have a unique one.
|
||||
// For example, `test cat` can use this to print encodings and register names.
|
||||
out.push((&**test, no_isa_flags, isa_spec.unique_isa()));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn run_one_test<'a>(
|
||||
tuple: (&'a SubTest, &'a Flags, Option<&'a TargetIsa>),
|
||||
func: Cow<Function>,
|
||||
context: &mut Context<'a>,
|
||||
) -> Result<()> {
|
||||
let (test, flags, isa) = tuple;
|
||||
let name = format!("{}({})", test.name(), func.name);
|
||||
dbg!("Test: {} {}", name, isa.map(TargetIsa::name).unwrap_or("-"));
|
||||
|
||||
context.flags = flags;
|
||||
context.isa = isa;
|
||||
|
||||
// Should we run the verifier before this test?
|
||||
if !context.verified && test.needs_verifier() {
|
||||
verify_function(&func, context.flags_or_isa()).map_err(
|
||||
|e| {
|
||||
pretty_verifier_error(&func, isa, e)
|
||||
},
|
||||
)?;
|
||||
context.verified = true;
|
||||
}
|
||||
|
||||
test.run(func, context).map_err(
|
||||
|e| format!("{}: {}", name, e),
|
||||
)
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
//! Test command for testing the simple GVN pass.
|
||||
//!
|
||||
//! The `simple-gvn` test command runs each function through the simple GVN pass after ensuring
|
||||
//! that all instructions are legal for the target.
|
||||
//!
|
||||
//! The resulting function is sent to `filecheck`.
|
||||
|
||||
use cretonne::ir::Function;
|
||||
use cretonne;
|
||||
use cton_reader::TestCommand;
|
||||
use filetest::subtest::{SubTest, Context, Result, run_filecheck};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
use utils::pretty_error;
|
||||
|
||||
struct TestSimpleGVN;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
|
||||
assert_eq!(parsed.command, "simple-gvn");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestSimpleGVN))
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestSimpleGVN {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::from("simple-gvn")
|
||||
}
|
||||
|
||||
fn is_mutating(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> Result<()> {
|
||||
// Create a compilation context, and drop in the function.
|
||||
let mut comp_ctx = cretonne::Context::new();
|
||||
comp_ctx.func = func.into_owned();
|
||||
|
||||
comp_ctx.flowgraph();
|
||||
comp_ctx.simple_gvn(context.flags_or_isa()).map_err(|e| {
|
||||
pretty_error(&comp_ctx.func, context.isa, Into::into(e))
|
||||
})?;
|
||||
|
||||
let mut text = String::new();
|
||||
write!(&mut text, "{}", &comp_ctx.func).map_err(
|
||||
|e| e.to_string(),
|
||||
)?;
|
||||
run_filecheck(&text, context)
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
//! SubTest trait.
|
||||
|
||||
use std::result;
|
||||
use std::borrow::Cow;
|
||||
use cretonne::ir::Function;
|
||||
use cretonne::isa::TargetIsa;
|
||||
use cretonne::settings::{Flags, FlagsOrIsa};
|
||||
use cton_reader::{Details, Comment};
|
||||
use filecheck::{CheckerBuilder, Checker, NO_VARIABLES};
|
||||
|
||||
pub type Result<T> = result::Result<T, String>;
|
||||
|
||||
/// Context for running a test on a single function.
|
||||
pub struct Context<'a> {
|
||||
/// Comments from the preamble f the test file. These apply to all functions.
|
||||
pub preamble_comments: &'a [Comment<'a>],
|
||||
|
||||
/// Additional details about the function from the parser.
|
||||
pub details: Details<'a>,
|
||||
|
||||
/// Was the function verified before running this test?
|
||||
pub verified: bool,
|
||||
|
||||
/// ISA-independent flags for this test.
|
||||
pub flags: &'a Flags,
|
||||
|
||||
/// Target ISA to test against. Only guaranteed to be present for sub-tests whose `needs_isa`
|
||||
/// method returned `true`. For other sub-tests, this is set if the test file has a unique ISA.
|
||||
pub isa: Option<&'a TargetIsa>,
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
/// Get a `FlagsOrIsa` object for passing to the verifier.
|
||||
pub fn flags_or_isa(&self) -> FlagsOrIsa<'a> {
|
||||
FlagsOrIsa {
|
||||
flags: self.flags,
|
||||
isa: self.isa,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Common interface for implementations of test commands.
|
||||
///
|
||||
/// Each `.cton` test file may contain multiple test commands, each represented by a `SubTest`
|
||||
/// trait object.
|
||||
pub trait SubTest {
|
||||
/// Name identifying this subtest. Typically the same as the test command.
|
||||
fn name(&self) -> Cow<str>;
|
||||
|
||||
/// Should the verifier be run on the function before running the test?
|
||||
fn needs_verifier(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Does this test mutate the function when it runs?
|
||||
/// This is used as a hint to avoid cloning the function needlessly.
|
||||
fn is_mutating(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Does this test need a `TargetIsa` trait object?
|
||||
fn needs_isa(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Run this test on `func`.
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Run filecheck on `text`, using directives extracted from `context`.
|
||||
pub fn run_filecheck(text: &str, context: &Context) -> Result<()> {
|
||||
let checker = build_filechecker(context)?;
|
||||
if checker.check(text, NO_VARIABLES).map_err(|e| {
|
||||
format!("filecheck: {}", e)
|
||||
})?
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
// Filecheck mismatch. Emit an explanation as output.
|
||||
let (_, explain) = checker.explain(text, NO_VARIABLES).map_err(|e| {
|
||||
format!("explain: {}", e)
|
||||
})?;
|
||||
Err(format!("filecheck failed:\n{}{}", checker, explain))
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a filechecker using the directives in the file preamble and the function's comments.
|
||||
pub fn build_filechecker(context: &Context) -> Result<Checker> {
|
||||
let mut builder = CheckerBuilder::new();
|
||||
// Preamble comments apply to all functions.
|
||||
for comment in context.preamble_comments {
|
||||
builder.directive(comment.text).map_err(|e| {
|
||||
format!("filecheck: {}", e)
|
||||
})?;
|
||||
}
|
||||
for comment in &context.details.comments {
|
||||
builder.directive(comment.text).map_err(|e| {
|
||||
format!("filecheck: {}", e)
|
||||
})?;
|
||||
}
|
||||
Ok(builder.finish())
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
//! Test command for checking the IL verifier.
|
||||
//!
|
||||
//! The `test verifier` test command looks for annotations on instructions like this:
|
||||
//!
|
||||
//! jump ebb3 ; error: jump to non-existent EBB
|
||||
//!
|
||||
//! This annotation means that the verifier is expected to given an error for the jump instruction
|
||||
//! containing the substring "jump to non-existent EBB".
|
||||
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use cretonne::verify_function;
|
||||
use cretonne::ir::Function;
|
||||
use cton_reader::TestCommand;
|
||||
use filetest::subtest::{SubTest, Context, Result};
|
||||
use utils::match_directive;
|
||||
|
||||
struct TestVerifier;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
|
||||
assert_eq!(parsed.command, "verifier");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestVerifier))
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestVerifier {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::from("verifier")
|
||||
}
|
||||
|
||||
fn needs_verifier(&self) -> bool {
|
||||
// Running the verifier before this test would defeat its purpose.
|
||||
false
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> Result<()> {
|
||||
let func = func.borrow();
|
||||
|
||||
// Scan source annotations for "error:" directives.
|
||||
let mut expected = None;
|
||||
for comment in &context.details.comments {
|
||||
if let Some(tail) = match_directive(comment.text, "error:") {
|
||||
// Currently, the verifier can only report one problem at a time.
|
||||
// Reject more than one `error:` directives.
|
||||
if expected.is_some() {
|
||||
return Err("cannot handle multiple error: directives".to_string());
|
||||
}
|
||||
expected = Some((comment.entity, tail));
|
||||
}
|
||||
}
|
||||
|
||||
match verify_function(func, context.flags_or_isa()) {
|
||||
Ok(_) => {
|
||||
match expected {
|
||||
None => Ok(()),
|
||||
Some((_, msg)) => Err(format!("passed, expected error: {}", msg)),
|
||||
}
|
||||
}
|
||||
Err(got) => {
|
||||
match expected {
|
||||
None => Err(format!("verifier pass, got {}", got)),
|
||||
Some((want_loc, want_msg)) if got.message.contains(want_msg) => {
|
||||
if want_loc == got.location {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"correct error reported on {}, but wanted {}",
|
||||
got.location,
|
||||
want_loc
|
||||
))
|
||||
}
|
||||
}
|
||||
Some(_) => Err(format!("mismatching error: {}", got)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,14 @@
|
||||
//! The `print-cfg` sub-command.
|
||||
//!
|
||||
//! Read a series of Cretonne IL files and print their control flow graphs
|
||||
//! Read a series of Cretonne IR files and print their control flow graphs
|
||||
//! in graphviz format.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{Result, Write, Display, Formatter};
|
||||
|
||||
use CommandResult;
|
||||
use cretonne::flowgraph::ControlFlowGraph;
|
||||
use cretonne::ir::Function;
|
||||
use cretonne::ir::instructions::BranchInfo;
|
||||
use cton_reader::{parse_functions, TestCommand};
|
||||
use filetest::subtest::{self, SubTest, Context, Result as STResult};
|
||||
use cretonne::cfg_printer::CFGPrinter;
|
||||
use cton_reader::parse_functions;
|
||||
use utils::read_to_string;
|
||||
|
||||
pub fn run(files: Vec<String>) -> CommandResult {
|
||||
pub fn run(files: &[String]) -> CommandResult {
|
||||
for (i, f) in files.into_iter().enumerate() {
|
||||
if i != 0 {
|
||||
println!();
|
||||
@@ -24,74 +18,8 @@ pub fn run(files: Vec<String>) -> CommandResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct CFGPrinter<'a> {
|
||||
func: &'a Function,
|
||||
cfg: ControlFlowGraph,
|
||||
}
|
||||
|
||||
impl<'a> CFGPrinter<'a> {
|
||||
pub fn new(func: &'a Function) -> CFGPrinter<'a> {
|
||||
CFGPrinter {
|
||||
func,
|
||||
cfg: ControlFlowGraph::with_function(func),
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the CFG for this function to `w`.
|
||||
pub fn write(&self, w: &mut Write) -> Result {
|
||||
self.header(w)?;
|
||||
self.ebb_nodes(w)?;
|
||||
self.cfg_connections(w)?;
|
||||
writeln!(w, "}}")
|
||||
}
|
||||
|
||||
fn header(&self, w: &mut Write) -> Result {
|
||||
writeln!(w, "digraph \"{}\" {{", self.func.name)?;
|
||||
if let Some(entry) = self.func.layout.entry_block() {
|
||||
writeln!(w, " {{rank=min; {}}}", entry)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ebb_nodes(&self, w: &mut Write) -> Result {
|
||||
for ebb in &self.func.layout {
|
||||
write!(w, " {} [shape=record, label=\"{{{}", ebb, ebb)?;
|
||||
// Add all outgoing branch instructions to the label.
|
||||
for inst in self.func.layout.ebb_insts(ebb) {
|
||||
let idata = &self.func.dfg[inst];
|
||||
match idata.analyze_branch(&self.func.dfg.value_lists) {
|
||||
BranchInfo::SingleDest(dest, _) => {
|
||||
write!(w, " | <{}>{} {}", inst, idata.opcode(), dest)?
|
||||
}
|
||||
BranchInfo::Table(table) => {
|
||||
write!(w, " | <{}>{} {}", inst, idata.opcode(), table)?
|
||||
}
|
||||
BranchInfo::NotABranch => {}
|
||||
}
|
||||
}
|
||||
writeln!(w, "}}\"]")?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cfg_connections(&self, w: &mut Write) -> Result {
|
||||
for ebb in &self.func.layout {
|
||||
for (parent, inst) in self.cfg.pred_iter(ebb) {
|
||||
writeln!(w, " {}:{} -> {}", parent, inst, ebb)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for CFGPrinter<'a> {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
self.write(f)
|
||||
}
|
||||
}
|
||||
|
||||
fn print_cfg(filename: String) -> CommandResult {
|
||||
let buffer = read_to_string(&filename).map_err(
|
||||
fn print_cfg(filename: &str) -> CommandResult {
|
||||
let buffer = read_to_string(filename).map_err(
|
||||
|e| format!("{}: {}", filename, e),
|
||||
)?;
|
||||
let items = parse_functions(&buffer).map_err(
|
||||
@@ -107,29 +35,3 @@ fn print_cfg(filename: String) -> CommandResult {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Object implementing the `test print-cfg` sub-test.
|
||||
struct TestPrintCfg;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> STResult<Box<SubTest>> {
|
||||
assert_eq!(parsed.command, "print-cfg");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestPrintCfg))
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestPrintCfg {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::from("print-cfg")
|
||||
}
|
||||
|
||||
fn needs_verifier(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> STResult<()> {
|
||||
subtest::run_filecheck(&CFGPrinter::new(&func).to_string(), context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
use CommandResult;
|
||||
use utils::read_to_string;
|
||||
use filecheck::{CheckerBuilder, Checker, NO_VARIABLES};
|
||||
use std::io::{self, Read};
|
||||
//! The `filecheck` sub-command.
|
||||
//!
|
||||
//! This file is named to avoid a name collision with the filecheck crate.
|
||||
|
||||
pub fn run(files: Vec<String>, verbose: bool) -> CommandResult {
|
||||
use CommandResult;
|
||||
use filecheck::{Checker, CheckerBuilder, NO_VARIABLES};
|
||||
use std::io::{self, Read};
|
||||
use utils::read_to_string;
|
||||
|
||||
pub fn run(files: &[String], verbose: bool) -> CommandResult {
|
||||
if files.is_empty() {
|
||||
return Err("No check files".to_string());
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
//! Utility functions.
|
||||
|
||||
use cretonne::ir::entities::AnyEntity;
|
||||
use cretonne::{ir, verifier};
|
||||
use cretonne::result::CtonError;
|
||||
use cretonne::isa;
|
||||
use cretonne::isa::TargetIsa;
|
||||
use cretonne::settings::{self, FlagsOrIsa};
|
||||
use cretonne::isa;
|
||||
use cton_reader::{parse_options, Location};
|
||||
use std::fmt::Write;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read};
|
||||
use std::path::Path;
|
||||
@@ -28,51 +24,6 @@ pub fn read_to_end<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
/// Look for a directive in a comment string.
|
||||
/// The directive is of the form "foo:" and should follow the leading `;` in the comment:
|
||||
///
|
||||
/// ; dominates: ebb3 ebb4
|
||||
///
|
||||
/// Return the comment text following the directive.
|
||||
pub fn match_directive<'a>(comment: &'a str, directive: &str) -> Option<&'a str> {
|
||||
assert!(
|
||||
directive.ends_with(':'),
|
||||
"Directive must include trailing colon"
|
||||
);
|
||||
let text = comment.trim_left_matches(';').trim_left();
|
||||
if text.starts_with(directive) {
|
||||
Some(text[directive.len()..].trim())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Pretty-print a verifier error.
|
||||
pub fn pretty_verifier_error(
|
||||
func: &ir::Function,
|
||||
isa: Option<&TargetIsa>,
|
||||
err: verifier::Error,
|
||||
) -> String {
|
||||
let mut msg = err.to_string();
|
||||
match err.location {
|
||||
AnyEntity::Inst(inst) => {
|
||||
write!(msg, "\n{}: {}\n\n", inst, func.dfg.display_inst(inst, isa)).unwrap()
|
||||
}
|
||||
_ => msg.push('\n'),
|
||||
}
|
||||
write!(msg, "{}", func.display(isa)).unwrap();
|
||||
msg
|
||||
}
|
||||
|
||||
/// Pretty-print a Cretonne error.
|
||||
pub fn pretty_error(func: &ir::Function, isa: Option<&TargetIsa>, err: CtonError) -> String {
|
||||
if let CtonError::Verifier(e) = err {
|
||||
pretty_verifier_error(func, isa, e)
|
||||
} else {
|
||||
err.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `FlagsOrIsa`, but holds ownership.
|
||||
pub enum OwnedFlagsOrIsa {
|
||||
Flags(settings::Flags),
|
||||
@@ -90,10 +41,7 @@ impl OwnedFlagsOrIsa {
|
||||
}
|
||||
|
||||
/// Parse "set" and "isa" commands.
|
||||
pub fn parse_sets_and_isa(
|
||||
flag_set: Vec<String>,
|
||||
flag_isa: String,
|
||||
) -> Result<OwnedFlagsOrIsa, String> {
|
||||
pub fn parse_sets_and_isa(flag_set: &[String], flag_isa: &str) -> Result<OwnedFlagsOrIsa, String> {
|
||||
let mut flag_builder = settings::builder();
|
||||
parse_options(
|
||||
flag_set.iter().map(|x| x.as_str()),
|
||||
@@ -119,12 +67,3 @@ pub fn parse_sets_and_isa(
|
||||
Ok(OwnedFlagsOrIsa::Flags(settings::Flags::new(&flag_builder)))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_match_directive() {
|
||||
assert_eq!(match_directive("; foo: bar ", "foo:"), Some("bar"));
|
||||
assert_eq!(match_directive(" foo:bar", "foo:"), Some("bar"));
|
||||
assert_eq!(match_directive("foo:bar", "foo:"), Some("bar"));
|
||||
assert_eq!(match_directive(";x foo: bar", "foo:"), None);
|
||||
assert_eq!(match_directive(";;; foo: bar", "foo:"), Some("bar"));
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
//! CLI tool to use the functions provided by the [cretonne-wasm](../cton_wasm/index.html) crate.
|
||||
//!
|
||||
//! Reads Wasm binary files, translates the functions' code to Cretonne IL.
|
||||
//! Reads Wasm binary files, translates the functions' code to Cretonne IR.
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments, cyclomatic_complexity))]
|
||||
|
||||
use cton_wasm::{translate_module, DummyEnvironment, ModuleEnvironment};
|
||||
use std::path::PathBuf;
|
||||
use cretonne::Context;
|
||||
use cretonne::print_errors::{pretty_error, pretty_verifier_error};
|
||||
use cretonne::settings::FlagsOrIsa;
|
||||
use std::fs::File;
|
||||
use cton_wasm::{translate_module, DummyEnvironment, ModuleEnvironment};
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use tempdir::TempDir;
|
||||
use term;
|
||||
use utils::{pretty_verifier_error, pretty_error, parse_sets_and_isa, read_to_end};
|
||||
use utils::{parse_sets_and_isa, read_to_end};
|
||||
|
||||
macro_rules! vprintln {
|
||||
($x: expr, $($tts:tt)*) => {
|
||||
@@ -37,8 +39,8 @@ pub fn run(
|
||||
flag_just_decode: bool,
|
||||
flag_check_translation: bool,
|
||||
flag_print: bool,
|
||||
flag_set: Vec<String>,
|
||||
flag_isa: String,
|
||||
flag_set: &[String],
|
||||
flag_isa: &str,
|
||||
flag_print_size: bool,
|
||||
) -> Result<(), String> {
|
||||
let parsed = parse_sets_and_isa(flag_set, flag_isa)?;
|
||||
@@ -52,8 +54,8 @@ pub fn run(
|
||||
flag_check_translation,
|
||||
flag_print,
|
||||
flag_print_size,
|
||||
path.to_path_buf(),
|
||||
name,
|
||||
&path.to_path_buf(),
|
||||
&name,
|
||||
parsed.as_fisa(),
|
||||
)?;
|
||||
}
|
||||
@@ -66,8 +68,8 @@ fn handle_module(
|
||||
flag_check_translation: bool,
|
||||
flag_print: bool,
|
||||
flag_print_size: bool,
|
||||
path: PathBuf,
|
||||
name: String,
|
||||
path: &PathBuf,
|
||||
name: &str,
|
||||
fisa: FlagsOrIsa,
|
||||
) -> Result<(), String> {
|
||||
let mut terminal = term::stdout().unwrap();
|
||||
@@ -152,29 +154,27 @@ fn handle_module(
|
||||
context.func = func.clone();
|
||||
if flag_check_translation {
|
||||
context.verify(fisa).map_err(|err| {
|
||||
pretty_verifier_error(&context.func, fisa.isa, err)
|
||||
pretty_verifier_error(&context.func, fisa.isa, &err)
|
||||
})?;
|
||||
} else {
|
||||
if let Some(isa) = fisa.isa {
|
||||
let compiled_size = context.compile(isa).map_err(|err| {
|
||||
pretty_error(&context.func, fisa.isa, err)
|
||||
})?;
|
||||
if flag_print_size {
|
||||
println!(
|
||||
"Function #{} code size: {} bytes",
|
||||
func_index,
|
||||
compiled_size
|
||||
);
|
||||
total_module_code_size += compiled_size;
|
||||
println!(
|
||||
"Function #{} bytecode size: {} bytes",
|
||||
func_index,
|
||||
dummy_environ.func_bytecode_sizes[func_index]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return Err(String::from("compilation requires a target isa"));
|
||||
} else if let Some(isa) = fisa.isa {
|
||||
let compiled_size = context.compile(isa).map_err(|err| {
|
||||
pretty_error(&context.func, fisa.isa, err)
|
||||
})?;
|
||||
if flag_print_size {
|
||||
println!(
|
||||
"Function #{} code size: {} bytes",
|
||||
func_index,
|
||||
compiled_size
|
||||
);
|
||||
total_module_code_size += compiled_size;
|
||||
println!(
|
||||
"Function #{} bytecode size: {} bytes",
|
||||
func_index,
|
||||
dummy_environ.func_bytecode_sizes[def_index]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return Err(String::from("compilation requires a target isa"));
|
||||
}
|
||||
if flag_print {
|
||||
vprintln!(flag_verbose, "");
|
||||
@@ -193,10 +193,7 @@ fn handle_module(
|
||||
|
||||
if !flag_check_translation && flag_print_size {
|
||||
println!("Total module code size: {} bytes", total_module_code_size);
|
||||
let total_bytecode_size = dummy_environ.func_bytecode_sizes.iter().fold(
|
||||
0,
|
||||
|sum, x| sum + x,
|
||||
);
|
||||
let total_bytecode_size: usize = dummy_environ.func_bytecode_sizes.iter().sum();
|
||||
println!("Total module bytecode size: {} bytes", total_bytecode_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,10 @@ set -euo pipefail
|
||||
|
||||
# This is the top-level test script:
|
||||
#
|
||||
# - Build documentation for Rust code in 'src/tools/target/doc'.
|
||||
# - Run unit tests for all Rust crates.
|
||||
# - Make a debug build of all crates.
|
||||
# - Make a release build of cton-util.
|
||||
# - Run file-level tests with the release build of cton-util.
|
||||
# - Make a debug build.
|
||||
# - Make a release build.
|
||||
# - Run unit tests for all Rust crates (including the filetests)
|
||||
# - Build API documentation.
|
||||
#
|
||||
# All tests run by this script should be passing at all times.
|
||||
|
||||
@@ -42,22 +41,26 @@ if [ -n "$needcheck" ]; then
|
||||
touch $tsfile || echo no target directory
|
||||
fi
|
||||
|
||||
cd "$topdir"
|
||||
banner "Rust unit tests"
|
||||
cargo test --all
|
||||
# Make sure the code builds in debug mode.
|
||||
banner "Rust debug build"
|
||||
cargo build
|
||||
|
||||
# Build cton-util for parser testing.
|
||||
cd "$topdir"
|
||||
banner "Rust documentation"
|
||||
echo "open $topdir/target/doc/cretonne/index.html"
|
||||
# Make sure the code builds in release mode, and run the unit tests. We run
|
||||
# these in release mode for speed, but note that the top-level Cargo.toml file
|
||||
# does enable debug assertions in release builds.
|
||||
banner "Rust release build and unit tests"
|
||||
cargo test --all --release
|
||||
|
||||
# Make sure the documentation builds.
|
||||
banner "Rust documentation: $topdir/target/doc/cretonne/index.html"
|
||||
cargo doc
|
||||
banner "Rust release build"
|
||||
cargo build --release
|
||||
|
||||
export CTONUTIL="$topdir/target/release/cton-util"
|
||||
|
||||
cd "$topdir"
|
||||
banner "File tests"
|
||||
"$CTONUTIL" test filetests docs
|
||||
# Run clippy if we have it.
|
||||
banner "Rust linter"
|
||||
if $topdir/check-clippy.sh; then
|
||||
$topdir/clippy-all.sh --write-mode=diff
|
||||
else
|
||||
echo "\`cargo +nightly install clippy\` for optional rust linting"
|
||||
fi
|
||||
|
||||
banner "OK"
|
||||
|
||||
7
cranelift/tests/filetests.rs
Normal file
7
cranelift/tests/filetests.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
extern crate cton_filetests;
|
||||
|
||||
#[test]
|
||||
fn filetests() {
|
||||
// Run all the filetests in the following directories.
|
||||
cton_filetests::run(false, &["filetests".into(), "docs".into()]).expect("test harness");
|
||||
}
|
||||
Reference in New Issue
Block a user