Merge branch 'master' into no_std

This commit is contained in:
morenzg
2018-04-17 16:41:27 -04:00
364 changed files with 3412 additions and 1024 deletions

View File

@@ -1,20 +1,18 @@
[package]
authors = ["The Cretonne Project Developers"]
name = "cretonne"
version = "0.4.1"
name = "cretonne-codegen"
version = "0.5.0"
description = "Low-level code generator library"
license = "Apache-2.0"
documentation = "https://cretonne.readthedocs.io/"
repository = "https://github.com/Cretonne/cretonne"
repository = "https://github.com/cretonne/cretonne"
readme = "README.md"
keywords = ["compile", "compiler", "jit"]
build = "build.rs"
[lib]
name = "cretonne"
[dependencies]
# It is a goal of the cretonne crate to have minimal external dependencies.
cretonne-entity = { path = "../entity", version = "0.5.0" }
# It is a goal of the cretonne-codegen crate to have minimal external dependencies.
# Please don't add any unless they are essential to the task of creating binary
# machine code. Integration tests that need external dependencies can be
# accomodated in `tests`.
@@ -35,4 +33,4 @@ core = ["hashmap_core"]
[badges]
maintenance = { status = "experimental" }
travis-ci = { repository = "Cretonne/cretonne" }
travis-ci = { repository = "cretonne/cretonne" }

View File

@@ -1,7 +1,7 @@
// Build script.
//
// This program is run by Cargo when building lib/cretonne. It is used to generate Rust code from
// the language definitions in the lib/cretonne/meta directory.
// This program is run by Cargo when building lib/codegen. It is used to generate Rust code from
// the language definitions in the lib/codegen/meta directory.
//
// Environment:
//
@@ -77,7 +77,7 @@ fn main() {
#[derive(Copy, Clone)]
enum Isa {
Riscv,
Intel,
X86,
Arm32,
Arm64,
}
@@ -103,14 +103,14 @@ impl Isa {
/// Returns all supported isa targets.
fn all() -> [Isa; 4] {
[Isa::Riscv, Isa::Intel, Isa::Arm32, Isa::Arm64]
[Isa::Riscv, Isa::X86, Isa::Arm32, Isa::Arm64]
}
/// Returns name of the isa target.
fn name(&self) -> &'static str {
match *self {
Isa::Riscv => "riscv",
Isa::Intel => "intel",
Isa::X86 => "x86",
Isa::Arm32 => "arm32",
Isa::Arm64 => "arm64",
}
@@ -120,7 +120,7 @@ impl Isa {
fn is_arch_applicable(&self, arch: &str) -> bool {
match *self {
Isa::Riscv => arch == "riscv",
Isa::Intel => ["x86_64", "i386", "i586", "i686"].contains(&arch),
Isa::X86 => ["x86_64", "i386", "i586", "i686"].contains(&arch),
Isa::Arm32 => arch.starts_with("arm") || arch.starts_with("thumb"),
Isa::Arm64 => arch == "aarch64",
}

View File

@@ -20,7 +20,7 @@ stack_slot = EntityRefKind('stack_slot', 'A stack slot.')
global_var = EntityRefKind('global_var', 'A global variable.')
#: A reference to a function sugnature declared in the function preamble.
#: Tbis is used to provide the call signature in an indirect call instruction.
#: This is used to provide the call signature in a call_indirect instruction.
sig_ref = EntityRefKind('sig_ref', 'A function signature.')
#: A reference to an external function declared in the function preamble.

View File

@@ -53,7 +53,7 @@ BranchIcmp = InstructionFormat(intcc, VALUE, VALUE, ebb, VARIABLE_ARGS)
BranchTable = InstructionFormat(VALUE, entities.jump_table)
Call = InstructionFormat(func_ref, VARIABLE_ARGS)
IndirectCall = InstructionFormat(sig_ref, VALUE, VARIABLE_ARGS)
CallIndirect = InstructionFormat(sig_ref, VALUE, VARIABLE_ARGS)
FuncAddr = InstructionFormat(func_ref)
Load = InstructionFormat(memflags, VALUE, offset32)

View File

@@ -65,6 +65,9 @@ expand_flags = XFormGroup('expand_flags', """
expand.custom_legalize(insts.global_addr, 'expand_global_addr')
expand.custom_legalize(insts.heap_addr, 'expand_heap_addr')
# Custom expansions for calls.
expand.custom_legalize(insts.call, 'expand_call')
# Custom expansions that need to change the CFG.
# TODO: Add sufficient XForm syntax that we don't need to hand-code these.
expand.custom_legalize(insts.trapz, 'expand_cond_trap')

View File

@@ -0,0 +1,35 @@
"""
Cretonne predicates that consider `Function` fields.
"""
from cdsl.predicates import FieldPredicate
from .formats import UnaryGlobalVar
try:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from cdsl.formats import FormatField # noqa
except ImportError:
pass
class IsColocatedFunc(FieldPredicate):
"""
An instruction predicate that checks the referenced function is colocated.
"""
def __init__(self, field):
# type: (FormatField) -> None
super(IsColocatedFunc, self).__init__(
field, 'is_colocated_func', ('func',))
class IsColocatedData(FieldPredicate):
"""
An instruction predicate that checks the referenced data object is
colocated.
"""
def __init__(self):
# type: () -> None
super(IsColocatedData, self).__init__(
UnaryGlobalVar.global_var, 'is_colocated_data', ('func',))

View File

@@ -29,6 +29,9 @@ enable_verifier = BoolSetting(
is_64bit = BoolSetting("Enable 64-bit code generation")
# Note that Cretonne doesn't currently need an is_pie flag, because PIE is just
# PIC where symbols can't be pre-empted, which can be expressed with the
# `colocated` flag on external functions and global variables.
is_pic = BoolSetting("Enable Position-Independent Code generation")
return_at_end = BoolSetting(
@@ -85,7 +88,7 @@ spiderwasm_prologue_words = NumSetting(
This setting configures the number of pointer-sized words pushed on the
stack when the Cretonne-generated code is entered. This includes the
pushed return address on Intel ISAs.
pushed return address on x86.
""")
#

View File

@@ -1,6 +1,6 @@
# Second-level build script.
#
# This script is run from lib/cretonne/build.rs to generate Rust files.
# This script is run from lib/codegen/build.rs to generate Rust files.
from __future__ import absolute_import
import argparse

View File

@@ -370,9 +370,9 @@ class TypePredicate(object):
"""
Return Rust code for evaluating this predicate.
It is assumed that the context has `dfg` and `args` variables.
It is assumed that the context has `func` and `args` variables.
"""
return 'dfg.value_type(args[{}]) == {}'.format(
return 'func.dfg.value_type(args[{}]) == {}'.format(
self.value_arg, self.value_type.rust_name())
@@ -409,7 +409,7 @@ class CtrlTypePredicate(object):
"""
Return Rust code for evaluating this predicate.
It is assumed that the context has `dfg` and `inst` variables.
It is assumed that the context has `func` and `inst` variables.
"""
return 'dfg.ctrl_typevar(inst) == {}'.format(
return 'func.dfg.ctrl_typevar(inst) == {}'.format(
self.value_type.rust_name())

View File

@@ -1,7 +1,7 @@
"""
Generate build dependencies for Cargo.
The `build.py` script is invoked by cargo when building lib/cretonne to
The `build.py` script is invoked by cargo when building lib/codegen to
generate Rust code from the instruction descriptions. Cargo needs to know when
it is necessary to rerun the build script.

View File

@@ -74,7 +74,7 @@ except ImportError:
pass
def emit_instp(instp, fmt, has_dfg=False):
def emit_instp(instp, fmt, has_func=False):
# type: (PredNode, srcgen.Formatter, bool) -> None
"""
Emit code for matching an instruction predicate against an
@@ -87,7 +87,7 @@ def emit_instp(instp, fmt, has_dfg=False):
# Deal with pure type check predicates which apply to any instruction.
if iform == instruction_context:
fmt.line('let args = inst.arguments(&dfg.value_lists);')
fmt.line('let args = inst.arguments(&func.dfg.value_lists);')
fmt.line(instp.rust_predicate(0))
return
@@ -114,11 +114,11 @@ def emit_instp(instp, fmt, has_dfg=False):
.format(iform.name, fields), '}'):
if has_type_check:
# We could implement this if we need to.
assert has_dfg, "Recipe predicates can't check type variables."
fmt.line('let args = inst.arguments(&dfg.value_lists);')
elif has_dfg:
assert has_func, "Recipe predicates can't check type variables."
fmt.line('let args = inst.arguments(&func.dfg.value_lists);')
elif has_func:
# Silence dead argument warning.
fmt.line('let _ = dfg;')
fmt.line('let _ = func;')
fmt.format('return {};', instp.rust_predicate(0))
fmt.line('unreachable!();')
@@ -132,9 +132,9 @@ def emit_inst_predicates(instps, fmt):
for instp, number in instps.items():
name = 'inst_predicate_{}'.format(number)
with fmt.indented(
'fn {}(dfg: &ir::DataFlowGraph, inst: &ir::InstructionData)'
'fn {}(func: &ir::Function, inst: &ir::InstructionData)'
'-> bool {{'.format(name), '}'):
emit_instp(instp, fmt, has_dfg=True)
emit_instp(instp, fmt, has_func=True)
# Generate the static table.
with fmt.indented(

View File

@@ -185,9 +185,9 @@ def unwrap_inst(iref, node, fmt):
fmt.line('ref args,')
fmt.line('..')
fmt.outdented_line('} = pos.func.dfg[inst] {')
fmt.line('let dfg = &pos.func.dfg;')
fmt.line('let func = &pos.func;')
if iform.has_value_list:
fmt.line('let args = args.as_slice(&dfg.value_lists);')
fmt.line('let args = args.as_slice(&func.dfg.value_lists);')
elif nvops == 1:
fmt.line('let args = [arg];')
# Generate the values for the tuple.
@@ -198,7 +198,7 @@ def unwrap_inst(iref, node, fmt):
fmt.format('{},', iform.imm_fields[n].member)
elif op.is_value():
n = expr.inst.value_opnums.index(opnum)
fmt.format('dfg.resolve_aliases(args[{}]),', n)
fmt.format('func.dfg.resolve_aliases(args[{}]),', n)
# Evaluate the instruction predicate, if any.
instp = expr.inst_predicate_with_ctrl_typevar()
fmt.line(instp.rust_predicate(0) if instp else 'true')

View File

@@ -2,7 +2,7 @@
Generate sources with type info.
This generates a `types.rs` file which is included in
`lib/cretonne/ir/types.rs`. The file provides constant definitions for the most
`lib/codegen/ir/types.rs`. The file provides constant definitions for the most
commonly used types, including all of the scalar types.
This ensures that Python and Rust use the same type numbering.

View File

@@ -7,7 +7,7 @@ architecture supported by Cretonne.
"""
from __future__ import absolute_import
from cdsl.isa import TargetISA # noqa
from . import riscv, intel, arm32, arm64
from . import riscv, x86, arm32, arm64
try:
from typing import List # noqa
@@ -21,4 +21,4 @@ def all_isas():
Get a list of all the supported target ISAs. Each target ISA is represented
as a :py:class:`cretonne.TargetISA` instance.
"""
return [riscv.ISA, intel.ISA, arm32.ISA, arm64.ISA]
return [riscv.ISA, x86.ISA, arm32.ISA, arm64.ISA]

View File

@@ -14,7 +14,7 @@ from cdsl.predicates import IsSignedInt
from cdsl.registers import Stack
from base.formats import Binary, BinaryImm, MultiAry, IntCompare, IntCompareImm
from base.formats import Unary, UnaryImm, BranchIcmp, Branch, Jump
from base.formats import Call, IndirectCall, RegMove
from base.formats import Call, CallIndirect, RegMove
from .registers import GPR
# The low 7 bits of a RISC-V instruction is the base opcode. All 32-bit
@@ -140,11 +140,11 @@ Iret = EncRecipe(
);
''')
# I-type encoding for `jalr` as an indirect call.
# I-type encoding for `jalr` as a call_indirect.
Icall = EncRecipe(
'Icall', IndirectCall, size=4, ins=GPR, outs=(),
'Icall', CallIndirect, size=4, ins=GPR, outs=(),
emit='''
// Indirect instructions are jalr with rd=%x1.
// call_indirect instructions are jalr with rd=%x1.
put_i(
bits,
in_reg0,

View File

@@ -0,0 +1,21 @@
"""
x86 Target Architecture
-------------------------
This target ISA generates code for x86 CPUs with two separate CPU modes:
`I32`
32-bit x86 architecture, also known as 'IA-32', also sometimes referred
to as 'i386', however note that Cretonne depends on instructions not
in the original `i386`, such as SSE2, CMOVcc, and UD2.
`I64`
x86-64 architecture, also known as 'AMD64`, `Intel 64`, and 'x64'.
"""
from __future__ import absolute_import
from . import defs
from . import encodings, settings, registers # noqa
# Re-export the primary target ISA definition.
ISA = defs.ISA.finish()

View File

@@ -1,5 +1,5 @@
"""
Intel definitions.
x86 definitions.
Commonly used definitions.
"""
@@ -9,7 +9,7 @@ import base.instructions
from . import instructions as x86
from base.immediates import floatcc
ISA = TargetISA('intel', [base.instructions.GROUP, x86.GROUP])
ISA = TargetISA('x86', [base.instructions.GROUP, x86.GROUP])
# CPU modes for 32-bit and 64-bit operation.
X86_64 = CPUMode('I64', ISA)

View File

@@ -1,15 +1,16 @@
"""
Intel Encodings.
x86 Encodings.
"""
from __future__ import absolute_import
from cdsl.predicates import IsUnsignedInt, Not, And
from base.predicates import IsColocatedFunc, IsColocatedData
from base import instructions as base
from base.formats import UnaryImm
from base.formats import UnaryImm, FuncAddr, Call
from .defs import X86_64, X86_32
from . import recipes as r
from . import settings as cfg
from . import instructions as x86
from .legalize import intel_expand
from .legalize import x86_expand
from base.legalize import narrow, expand_flags
from base.settings import allones_funcaddrs, is_pic
from .settings import use_sse41
@@ -26,18 +27,18 @@ X86_32.legalize_monomorphic(expand_flags)
X86_32.legalize_type(
default=narrow,
b1=expand_flags,
i32=intel_expand,
f32=intel_expand,
f64=intel_expand)
i32=x86_expand,
f32=x86_expand,
f64=x86_expand)
X86_64.legalize_monomorphic(expand_flags)
X86_64.legalize_type(
default=narrow,
b1=expand_flags,
i32=intel_expand,
i64=intel_expand,
f32=intel_expand,
f64=intel_expand)
i32=x86_expand,
i64=x86_expand,
f32=x86_expand,
f64=x86_expand)
#
@@ -292,16 +293,24 @@ enc_both(base.regspill.f64, r.fregspill32, 0xf2, 0x0f, 0x11)
# Function addresses.
#
# Non-PIC, all-ones funcaddresses.
X86_32.enc(base.func_addr.i32, *r.fnaddr4(0xb8),
isap=Not(allones_funcaddrs))
isap=And(Not(allones_funcaddrs), Not(is_pic)))
X86_64.enc(base.func_addr.i64, *r.fnaddr8.rex(0xb8, w=1),
isap=And(Not(allones_funcaddrs), Not(is_pic)))
# Non-PIC, all-zeros funcaddresses.
X86_32.enc(base.func_addr.i32, *r.allones_fnaddr4(0xb8),
isap=allones_funcaddrs)
isap=And(allones_funcaddrs, Not(is_pic)))
X86_64.enc(base.func_addr.i64, *r.allones_fnaddr8.rex(0xb8, w=1),
isap=And(allones_funcaddrs, Not(is_pic)))
# 64-bit, colocated, both PIC and non-PIC. Use the lea instruction's
# pc-relative field.
X86_64.enc(base.func_addr.i64, *r.pcrel_fnaddr8.rex(0x8d, w=1),
instp=IsColocatedFunc(FuncAddr.func_ref))
# 64-bit, non-colocated, PIC.
X86_64.enc(base.func_addr.i64, *r.got_fnaddr8.rex(0x8b, w=1),
isap=is_pic)
@@ -309,18 +318,36 @@ X86_64.enc(base.func_addr.i64, *r.got_fnaddr8.rex(0x8b, w=1),
# Global addresses.
#
X86_32.enc(base.globalsym_addr.i32, *r.gvaddr4(0xb8))
# Non-PIC
X86_32.enc(base.globalsym_addr.i32, *r.gvaddr4(0xb8),
isap=Not(is_pic))
X86_64.enc(base.globalsym_addr.i64, *r.gvaddr8.rex(0xb8, w=1),
isap=Not(is_pic))
# PIC, colocated
X86_64.enc(base.globalsym_addr.i64, *r.pcrel_gvaddr8.rex(0x8d, w=1),
isap=is_pic,
instp=IsColocatedData())
# PIC, non-colocated
X86_64.enc(base.globalsym_addr.i64, *r.got_gvaddr8.rex(0x8b, w=1),
isap=is_pic)
#
# Call/return
#
# 32-bit, both PIC and non-PIC.
X86_32.enc(base.call, *r.call_id(0xe8))
X86_64.enc(base.call, *r.call_id(0xe8), isap=Not(is_pic))
# 64-bit, colocated, both PIC and non-PIC. Use the call instruction's
# pc-relative field.
X86_64.enc(base.call, *r.call_id(0xe8),
instp=IsColocatedFunc(Call.func_ref))
# 64-bit, non-colocated, PIC. There is no 64-bit non-colocated non-PIC version,
# since non-PIC is currently using the large model, which requires calls be
# lowered to func_addr+call_indirect.
X86_64.enc(base.call, *r.call_plt_id(0xe8), isap=is_pic)
X86_32.enc(base.call_indirect.i32, *r.call_r(0xff, rrr=2))

View File

@@ -1,7 +1,7 @@
"""
Supplementary instruction definitions for Intel.
Supplementary instruction definitions for x86.
This module defines additional instructions that are useful only to the Intel
This module defines additional instructions that are useful only to the x86
target ISA.
"""
@@ -11,7 +11,7 @@ from cdsl.typevar import TypeVar
from cdsl.instructions import Instruction, InstructionGroup
GROUP = InstructionGroup("x86", "Intel-specific instruction set")
GROUP = InstructionGroup("x86", "x86-specific instruction set")
iWord = TypeVar('iWord', 'A scalar integer machine word', ints=(32, 64))
@@ -98,7 +98,7 @@ y = Operand('y', Float)
fmin = Instruction(
'x86_fmin', r"""
Floating point minimum with Intel semantics.
Floating point minimum with x86 semantics.
This is equivalent to the C ternary operator `x < y ? x : y` which
differs from :inst:`fmin` when either operand is NaN or when comparing
@@ -111,7 +111,7 @@ fmin = Instruction(
fmax = Instruction(
'x86_fmax', r"""
Floating point maximum with Intel semantics.
Floating point maximum with x86 semantics.
This is equivalent to the C ternary operator `x > y ? x : y` which
differs from :inst:`fmax` when either operand is NaN or when comparing

View File

@@ -1,5 +1,5 @@
"""
Custom legalization patterns for Intel.
Custom legalization patterns for x86.
"""
from __future__ import absolute_import
from cdsl.ast import Var
@@ -10,12 +10,12 @@ from base import instructions as insts
from . import instructions as x86
from .defs import ISA
intel_expand = XFormGroup(
'intel_expand',
x86_expand = XFormGroup(
'x86_expand',
"""
Legalize instructions by expansion.
Use Intel-specific instructions if needed.
Use x86-specific instructions if needed.
""",
isa=ISA, chain=shared.expand_flags)
@@ -32,23 +32,23 @@ a2 = Var('a2')
#
# The srem expansion requires custom code because srem INT_MIN, -1 is not
# allowed to trap. The other ops need to check avoid_div_traps.
intel_expand.custom_legalize(insts.sdiv, 'expand_sdivrem')
intel_expand.custom_legalize(insts.srem, 'expand_sdivrem')
intel_expand.custom_legalize(insts.udiv, 'expand_udivrem')
intel_expand.custom_legalize(insts.urem, 'expand_udivrem')
x86_expand.custom_legalize(insts.sdiv, 'expand_sdivrem')
x86_expand.custom_legalize(insts.srem, 'expand_sdivrem')
x86_expand.custom_legalize(insts.udiv, 'expand_udivrem')
x86_expand.custom_legalize(insts.urem, 'expand_udivrem')
#
# Double length (widening) multiplication
#
resLo = Var('resLo')
resHi = Var('resHi')
intel_expand.legalize(
x86_expand.legalize(
resHi << insts.umulhi(x, y),
Rtl(
(resLo, resHi) << x86.umulx(x, y)
))
intel_expand.legalize(
x86_expand.legalize(
resHi << insts.smulhi(x, y),
Rtl(
(resLo, resHi) << x86.smulx(x, y)
@@ -61,14 +61,14 @@ intel_expand.legalize(
# patterns.
# Equality needs an explicit `ord` test which checks the parity bit.
intel_expand.legalize(
x86_expand.legalize(
a << insts.fcmp(floatcc.eq, x, y),
Rtl(
a1 << insts.fcmp(floatcc.ord, x, y),
a2 << insts.fcmp(floatcc.ueq, x, y),
a << insts.band(a1, a2)
))
intel_expand.legalize(
x86_expand.legalize(
a << insts.fcmp(floatcc.ne, x, y),
Rtl(
a1 << insts.fcmp(floatcc.uno, x, y),
@@ -82,21 +82,21 @@ for cc, rev_cc in [
(floatcc.le, floatcc.ge),
(floatcc.ugt, floatcc.ult),
(floatcc.uge, floatcc.ule)]:
intel_expand.legalize(
x86_expand.legalize(
a << insts.fcmp(cc, x, y),
Rtl(
a << insts.fcmp(rev_cc, y, x)
))
# We need to modify the CFG for min/max legalization.
intel_expand.custom_legalize(insts.fmin, 'expand_minmax')
intel_expand.custom_legalize(insts.fmax, 'expand_minmax')
x86_expand.custom_legalize(insts.fmin, 'expand_minmax')
x86_expand.custom_legalize(insts.fmax, 'expand_minmax')
# Conversions from unsigned need special handling.
intel_expand.custom_legalize(insts.fcvt_from_uint, 'expand_fcvt_from_uint')
x86_expand.custom_legalize(insts.fcvt_from_uint, 'expand_fcvt_from_uint')
# Conversions from float to int can trap.
intel_expand.custom_legalize(insts.fcvt_to_sint, 'expand_fcvt_to_sint')
intel_expand.custom_legalize(insts.fcvt_to_uint, 'expand_fcvt_to_uint')
x86_expand.custom_legalize(insts.fcvt_to_sint, 'expand_fcvt_to_sint')
x86_expand.custom_legalize(insts.fcvt_to_uint, 'expand_fcvt_to_uint')
# Count leading and trailing zeroes, for baseline x86_64
c_minus_one = Var('c_minus_one')
@@ -108,7 +108,7 @@ index1 = Var('index1')
r2flags = Var('r2flags')
index2 = Var('index2')
intel_expand.legalize(
x86_expand.legalize(
a << insts.clz.i64(x),
Rtl(
c_minus_one << insts.iconst(imm64(-1)),
@@ -118,7 +118,7 @@ intel_expand.legalize(
a << insts.isub(c_sixty_three, index2),
))
intel_expand.legalize(
x86_expand.legalize(
a << insts.clz.i32(x),
Rtl(
c_minus_one << insts.iconst(imm64(-1)),
@@ -128,7 +128,7 @@ intel_expand.legalize(
a << insts.isub(c_thirty_one, index2),
))
intel_expand.legalize(
x86_expand.legalize(
a << insts.ctz.i64(x),
Rtl(
c_sixty_four << insts.iconst(imm64(64)),
@@ -136,7 +136,7 @@ intel_expand.legalize(
a << insts.selectif(intcc.eq, r2flags, c_sixty_four, index1),
))
intel_expand.legalize(
x86_expand.legalize(
a << insts.ctz.i32(x),
Rtl(
c_thirty_two << insts.iconst(imm64(32)),
@@ -164,7 +164,7 @@ qv16 = Var('qv16')
qc77 = Var('qc77')
qc0F = Var('qc0F')
qc01 = Var('qc01')
intel_expand.legalize(
x86_expand.legalize(
qv16 << insts.popcnt.i64(qv1),
Rtl(
qv3 << insts.ushr_imm(qv1, imm64(1)),
@@ -204,7 +204,7 @@ lv16 = Var('lv16')
lc77 = Var('lc77')
lc0F = Var('lc0F')
lc01 = Var('lc01')
intel_expand.legalize(
x86_expand.legalize(
lv16 << insts.popcnt.i32(lv1),
Rtl(
lv3 << insts.ushr_imm(lv1, imm64(1)),

View File

@@ -1,5 +1,5 @@
"""
Intel Encoding recipes.
x86 Encoding recipes.
"""
from __future__ import absolute_import
from cdsl.isa import EncRecipe
@@ -7,7 +7,7 @@ from cdsl.predicates import IsSignedInt, IsEqual, Or
from cdsl.registers import RegClass
from base.formats import Unary, UnaryImm, UnaryBool, Binary, BinaryImm
from base.formats import MultiAry, NullAry
from base.formats import Trap, Call, IndirectCall, Store, Load
from base.formats import Trap, Call, CallIndirect, Store, Load
from base.formats import IntCompare, IntCompareImm, FloatCompare
from base.formats import IntCond, FloatCond
from base.formats import IntSelect, IntCondTrap, FloatCondTrap
@@ -31,7 +31,7 @@ except ImportError:
# Opcode representation.
#
# Cretonne requires each recipe to have a single encoding size in bytes, and
# Intel opcodes are variable length, so we use separate recipes for different
# x86 opcodes are variable length, so we use separate recipes for different
# styles of opcodes and prefixes. The opcode format is indicated by the recipe
# name prefix:
@@ -124,7 +124,7 @@ class TailRecipe:
"""
Generate encoding recipes on demand.
Intel encodings are somewhat orthogonal with the opcode representation on
x86 encodings are somewhat orthogonal with the opcode representation on
one side and the ModR/M, SIB and immediate fields on the other side.
A `TailRecipe` represents the part of an encoding that follow the opcode.
@@ -144,7 +144,7 @@ class TailRecipe:
The `emit` parameter contains Rust code to actually emit an encoding, like
`EncRecipe` does it. Additionally, the text `PUT_OP` is substituted with
the proper `put_*` function from the `intel/binemit.rs` module.
the proper `put_*` function from the `x86/binemit.rs` module.
"""
def __init__(
@@ -590,7 +590,7 @@ fnaddr4 = TailRecipe(
'fnaddr4', FuncAddr, size=4, ins=(), outs=GPR,
emit='''
PUT_OP(bits | (out_reg0 & 7), rex1(out_reg0), sink);
sink.reloc_external(Reloc::IntelAbs4,
sink.reloc_external(Reloc::Abs4,
&func.dfg.ext_funcs[func_ref].name,
0);
sink.put4(0);
@@ -601,7 +601,7 @@ fnaddr8 = TailRecipe(
'fnaddr8', FuncAddr, size=8, ins=(), outs=GPR,
emit='''
PUT_OP(bits | (out_reg0 & 7), rex1(out_reg0), sink);
sink.reloc_external(Reloc::IntelAbs8,
sink.reloc_external(Reloc::Abs8,
&func.dfg.ext_funcs[func_ref].name,
0);
sink.put8(0);
@@ -612,7 +612,7 @@ allones_fnaddr4 = TailRecipe(
'allones_fnaddr4', FuncAddr, size=4, ins=(), outs=GPR,
emit='''
PUT_OP(bits | (out_reg0 & 7), rex1(out_reg0), sink);
sink.reloc_external(Reloc::IntelAbs4,
sink.reloc_external(Reloc::Abs4,
&func.dfg.ext_funcs[func_ref].name,
0);
// Write the immediate as `!0` for the benefit of BaldrMonkey.
@@ -624,13 +624,28 @@ allones_fnaddr8 = TailRecipe(
'allones_fnaddr8', FuncAddr, size=8, ins=(), outs=GPR,
emit='''
PUT_OP(bits | (out_reg0 & 7), rex1(out_reg0), sink);
sink.reloc_external(Reloc::IntelAbs8,
sink.reloc_external(Reloc::Abs8,
&func.dfg.ext_funcs[func_ref].name,
0);
// Write the immediate as `!0` for the benefit of BaldrMonkey.
sink.put8(!0);
''')
pcrel_fnaddr8 = TailRecipe(
'pcrel_fnaddr8', FuncAddr, size=5, ins=(), outs=GPR,
# rex2 gets passed 0 for r/m register because the upper bit of
# r/m doesnt get decoded when in rip-relative addressing mode.
emit='''
PUT_OP(bits, rex2(0, out_reg0), sink);
modrm_riprel(out_reg0, sink);
// The addend adjusts for the difference between the end of the
// instruction and the beginning of the immediate field.
sink.reloc_external(Reloc::X86PCRel4,
&func.dfg.ext_funcs[func_ref].name,
-4);
sink.put4(0);
''')
got_fnaddr8 = TailRecipe(
'got_fnaddr8', FuncAddr, size=5, ins=(), outs=GPR,
# rex2 gets passed 0 for r/m register because the upper bit of
@@ -640,7 +655,7 @@ got_fnaddr8 = TailRecipe(
modrm_riprel(out_reg0, sink);
// The addend adjusts for the difference between the end of the
// instruction and the beginning of the immediate field.
sink.reloc_external(Reloc::IntelGOTPCRel4,
sink.reloc_external(Reloc::X86GOTPCRel4,
&func.dfg.ext_funcs[func_ref].name,
-4);
sink.put4(0);
@@ -652,7 +667,7 @@ gvaddr4 = TailRecipe(
'gvaddr4', UnaryGlobalVar, size=4, ins=(), outs=GPR,
emit='''
PUT_OP(bits | (out_reg0 & 7), rex1(out_reg0), sink);
sink.reloc_external(Reloc::IntelAbs4,
sink.reloc_external(Reloc::Abs4,
&func.global_vars[global_var].symbol_name(),
0);
sink.put4(0);
@@ -663,12 +678,26 @@ gvaddr8 = TailRecipe(
'gvaddr8', UnaryGlobalVar, size=8, ins=(), outs=GPR,
emit='''
PUT_OP(bits | (out_reg0 & 7), rex1(out_reg0), sink);
sink.reloc_external(Reloc::IntelAbs8,
sink.reloc_external(Reloc::Abs8,
&func.global_vars[global_var].symbol_name(),
0);
sink.put8(0);
''')
# XX+rd iq with PCRel4 globalsym relocation.
pcrel_gvaddr8 = TailRecipe(
'pcrel_gvaddr8', UnaryGlobalVar, size=5, ins=(), outs=GPR,
emit='''
PUT_OP(bits, rex2(0, out_reg0), sink);
modrm_rm(5, out_reg0, sink);
// The addend adjusts for the difference between the end of the
// instruction and the beginning of the immediate field.
sink.reloc_external(Reloc::X86PCRel4,
&func.global_vars[global_var].symbol_name(),
-4);
sink.put4(0);
''')
# XX+rd iq with Abs8 globalsym relocation.
got_gvaddr8 = TailRecipe(
'got_gvaddr8', UnaryGlobalVar, size=5, ins=(), outs=GPR,
@@ -677,7 +706,7 @@ got_gvaddr8 = TailRecipe(
modrm_rm(5, out_reg0, sink);
// The addend adjusts for the difference between the end of the
// instruction and the beginning of the immediate field.
sink.reloc_external(Reloc::IntelGOTPCRel4,
sink.reloc_external(Reloc::X86GOTPCRel4,
&func.global_vars[global_var].symbol_name(),
-4);
sink.put4(0);
@@ -1007,9 +1036,11 @@ call_id = TailRecipe(
'call_id', Call, size=4, ins=(), outs=(),
emit='''
PUT_OP(bits, BASE_REX, sink);
sink.reloc_external(Reloc::IntelPCRel4,
// The addend adjusts for the difference between the end of the
// instruction and the beginning of the immediate field.
sink.reloc_external(Reloc::X86PCRel4,
&func.dfg.ext_funcs[func_ref].name,
0);
-4);
sink.put4(0);
''')
@@ -1017,14 +1048,14 @@ call_plt_id = TailRecipe(
'call_plt_id', Call, size=4, ins=(), outs=(),
emit='''
PUT_OP(bits, BASE_REX, sink);
sink.reloc_external(Reloc::IntelPLTRel4,
sink.reloc_external(Reloc::X86PLTRel4,
&func.dfg.ext_funcs[func_ref].name,
-4);
sink.put4(0);
''')
call_r = TailRecipe(
'call_r', IndirectCall, size=1, ins=GPR, outs=(),
'call_r', CallIndirect, size=1, ins=GPR, outs=(),
emit='''
PUT_OP(bits, rex1(in_reg0), sink);
modrm_r_bits(in_reg0, bits, sink);

View File

@@ -1,9 +1,9 @@
"""
Intel register banks.
x86 register banks.
While the floating-point registers are straight-forward, the general purpose
register bank has a few quirks on Intel architectures. We have these encodings
of the 8-bit registers:
register bank has a few quirks on x86. We have these encodings of the 8-bit
registers:
I32 I64 | 16b 32b 64b
000 AL AL | AX EAX RAX

View File

@@ -1,5 +1,5 @@
"""
Intel settings.
x86 settings.
"""
from __future__ import absolute_import
from cdsl.settings import SettingGroup, BoolSetting, Preset
@@ -7,7 +7,7 @@ from cdsl.predicates import And
import base.settings as shared
from .defs import ISA
ISA.settings = SettingGroup('intel', parent=shared.group)
ISA.settings = SettingGroup('x86', parent=shared.group)
# The has_* settings here correspond to CPUID bits.
@@ -35,7 +35,7 @@ use_popcnt = And(has_popcnt, has_sse42)
use_bmi1 = And(has_bmi1)
use_lzcnt = And(has_lzcnt)
# Presets corresponding to Intel CPUs.
# Presets corresponding to x86 CPUs.
baseline = Preset()
nehalem = Preset(

View File

@@ -123,3 +123,11 @@ impl<'a> CodeSink for MemoryCodeSink<'a> {
self.traps.trap(ofs, srcloc, code);
}
}
/// A `TrapSink` implementation that does nothing, which is convenient when
/// compiling code that does not rely on trapping semantics.
pub struct NullTrapSink {}
impl TrapSink for NullTrapSink {
fn trap(&mut self, _offset: CodeOffset, _srcloc: SourceLoc, _code: TrapCode) {}
}

View File

@@ -6,7 +6,7 @@
mod memorysink;
mod relaxation;
pub use self::memorysink::{MemoryCodeSink, RelocSink, TrapSink};
pub use self::memorysink::{MemoryCodeSink, RelocSink, TrapSink, NullTrapSink};
pub use self::relaxation::relax_branches;
pub use regalloc::RegDiversions;
@@ -23,18 +23,18 @@ pub type CodeOffset = u32;
pub type Addend = i64;
/// Relocation kinds for every ISA
#[derive(Debug)]
#[derive(Copy, Clone, Debug)]
pub enum Reloc {
/// Intel PC-relative 4-byte
IntelPCRel4,
/// Intel absolute 4-byte
IntelAbs4,
/// Intel absolute 8-byte
IntelAbs8,
/// Intel GOT PC-relative 4-byte
IntelGOTPCRel4,
/// Intel PLT-relative 4-byte
IntelPLTRel4,
/// absolute 4-byte
Abs4,
/// absolute 8-byte
Abs8,
/// x86 PC-relative 4-byte
X86PCRel4,
/// x86 GOT PC-relative 4-byte
X86GOTPCRel4,
/// x86 PLT-relative 4-byte
X86PLTRel4,
/// Arm32 call target
Arm32Call,
/// Arm64 call target
@@ -48,11 +48,11 @@ impl fmt::Display for Reloc {
/// already unambigious, e.g. cton syntax with isa specified. In other contexts, use Debug.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Reloc::IntelPCRel4 => write!(f, "{}", "PCRel4"),
Reloc::IntelAbs4 => write!(f, "{}", "Abs4"),
Reloc::IntelAbs8 => write!(f, "{}", "Abs8"),
Reloc::IntelGOTPCRel4 => write!(f, "{}", "GOTPCRel4"),
Reloc::IntelPLTRel4 => write!(f, "{}", "PLTRel4"),
Reloc::Abs4 => write!(f, "{}", "Abs4"),
Reloc::Abs8 => write!(f, "{}", "Abs8"),
Reloc::X86PCRel4 => write!(f, "{}", "PCRel4"),
Reloc::X86GOTPCRel4 => write!(f, "{}", "GOTPCRel4"),
Reloc::X86PLTRel4 => write!(f, "{}", "PLTRel4"),
Reloc::Arm32Call | Reloc::Arm64Call | Reloc::RiscvCall => write!(f, "{}", "Call"),
}
}

View File

@@ -10,7 +10,7 @@
//!
//! Branch relaxation is the process of ensuring that all branches in the function have enough
//! range to encode their destination. It is common to have multiple branch encodings in an ISA.
//! For example, Intel branches can have either an 8-bit or a 32-bit displacement.
//! For example, x86 branches can have either an 8-bit or a 32-bit displacement.
//!
//! On RISC architectures, it can happen that conditional branches have a shorter range than
//! unconditional branches:
@@ -148,7 +148,7 @@ fn relax_branch(
// Pick the first encoding that can handle the branch range.
let dfg = &cur.func.dfg;
let ctrl_type = dfg.ctrl_typevar(inst);
if let Some(enc) = isa.legal_encodings(dfg, &dfg[inst], ctrl_type).find(
if let Some(enc) = isa.legal_encodings(cur.func, &dfg[inst], ctrl_type).find(
|&enc| {
let range = encinfo.branch_range(enc).expect("Branch with no range");
if !range.contains(offset, dest_offset) {

View File

@@ -1,6 +1,6 @@
//! Runtime support for precomputed constant hash tables.
//!
//! The `lib/cretonne/meta/constant_hash.py` Python module can generate constant hash tables using
//! The `lib/codegen/meta/constant_hash.py` Python module can generate constant hash tables using
//! open addressing and quadratic probing. The hash tables are arrays that are guaranteed to:
//!
//! - Have a power-of-two size.
@@ -56,7 +56,7 @@ pub fn probe<K: Copy + Eq, T: Table<K> + ?Sized>(
}
/// A primitive hash function for matching opcodes.
/// Must match `lib/cretonne/meta/constant_hash.py`.
/// Must match `lib/codegen/meta/constant_hash.py`.
pub fn simple_hash(s: &str) -> usize {
let mut h: u32 = 5381;
for c in s.chars() {

View File

@@ -46,8 +46,8 @@ pub trait Cursor {
/// This is intended to be used as a builder method:
///
/// ```
/// # use cretonne::ir::{Function, Ebb, SourceLoc};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb, SourceLoc};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_func(func: &mut Function, srcloc: SourceLoc) {
/// let mut pos = FuncCursor::new(func).with_srcloc(srcloc);
///
@@ -76,8 +76,8 @@ pub trait Cursor {
/// This is intended to be used as a builder method:
///
/// ```
/// # use cretonne::ir::{Function, Ebb, Inst};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb, Inst};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_func(func: &mut Function, inst: Inst) {
/// let mut pos = FuncCursor::new(func).at_inst(inst);
///
@@ -99,8 +99,8 @@ pub trait Cursor {
/// This is intended to be used as a builder method:
///
/// ```
/// # use cretonne::ir::{Function, Ebb, Inst};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb, Inst};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_func(func: &mut Function, ebb: Ebb) {
/// let mut pos = FuncCursor::new(func).at_first_insertion_point(ebb);
///
@@ -120,8 +120,8 @@ pub trait Cursor {
/// This is intended to be used as a builder method:
///
/// ```
/// # use cretonne::ir::{Function, Ebb, Inst};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb, Inst};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_func(func: &mut Function, ebb: Ebb) {
/// let mut pos = FuncCursor::new(func).at_first_inst(ebb);
///
@@ -141,8 +141,8 @@ pub trait Cursor {
/// This is intended to be used as a builder method:
///
/// ```
/// # use cretonne::ir::{Function, Ebb, Inst};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb, Inst};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_func(func: &mut Function, ebb: Ebb) {
/// let mut pos = FuncCursor::new(func).at_last_inst(ebb);
///
@@ -162,8 +162,8 @@ pub trait Cursor {
/// This is intended to be used as a builder method:
///
/// ```
/// # use cretonne::ir::{Function, Ebb, Inst};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb, Inst};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_func(func: &mut Function, inst: Inst) {
/// let mut pos = FuncCursor::new(func).after_inst(inst);
///
@@ -183,8 +183,8 @@ pub trait Cursor {
/// This is intended to be used as a builder method:
///
/// ```
/// # use cretonne::ir::{Function, Ebb, Inst};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb, Inst};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_func(func: &mut Function, ebb: Ebb) {
/// let mut pos = FuncCursor::new(func).at_top(ebb);
///
@@ -204,8 +204,8 @@ pub trait Cursor {
/// This is intended to be used as a builder method:
///
/// ```
/// # use cretonne::ir::{Function, Ebb, Inst};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb, Inst};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_func(func: &mut Function, ebb: Ebb) {
/// let mut pos = FuncCursor::new(func).at_bottom(ebb);
///
@@ -309,8 +309,8 @@ pub trait Cursor {
/// The `next_ebb()` method is intended for iterating over the EBBs in layout order:
///
/// ```
/// # use cretonne::ir::{Function, Ebb};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_func(func: &mut Function) {
/// let mut cursor = FuncCursor::new(func);
/// while let Some(ebb) = cursor.next_ebb() {
@@ -342,8 +342,8 @@ pub trait Cursor {
/// The `prev_ebb()` method is intended for iterating over the EBBs in backwards layout order:
///
/// ```
/// # use cretonne::ir::{Function, Ebb};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_func(func: &mut Function) {
/// let mut cursor = FuncCursor::new(func);
/// while let Some(ebb) = cursor.prev_ebb() {
@@ -379,8 +379,8 @@ pub trait Cursor {
/// this:
///
/// ```
/// # use cretonne::ir::{Function, Ebb};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_ebb(func: &mut Function, ebb: Ebb) {
/// let mut cursor = FuncCursor::new(func).at_top(ebb);
/// while let Some(inst) = cursor.next_inst() {
@@ -393,8 +393,8 @@ pub trait Cursor {
/// Iterating over all the instructions in a function looks like this:
///
/// ```
/// # use cretonne::ir::{Function, Ebb};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_func(func: &mut Function) {
/// let mut cursor = FuncCursor::new(func);
/// while let Some(ebb) = cursor.next_ebb() {
@@ -447,8 +447,8 @@ pub trait Cursor {
/// EBB like this:
///
/// ```
/// # use cretonne::ir::{Function, Ebb};
/// # use cretonne::cursor::{Cursor, FuncCursor};
/// # use cretonne_codegen::ir::{Function, Ebb};
/// # use cretonne_codegen::cursor::{Cursor, FuncCursor};
/// fn edit_ebb(func: &mut Function, ebb: Ebb) {
/// let mut cursor = FuncCursor::new(func).at_bottom(ebb);
/// while let Some(inst) = cursor.prev_inst() {
@@ -747,7 +747,7 @@ impl<'c, 'f> ir::InstInserterBase<'c> for &'c mut EncCursor<'f> {
// XXX Is there a way to describe this error to the user?
#[cfg_attr(feature = "cargo-clippy", allow(match_wild_err_arm))]
match self.isa.encode(
&self.func.dfg,
&self.func,
&self.func.dfg[inst],
ctrl_typevar,
) {

Some files were not shown because too many files have changed in this diff Show More