lib/codegen-meta moved into lib/codegen. (#423)
* lib/codegen-meta moved into lib/codegen. * Renamed codegen-meta and existing meta.
This commit is contained in:
1
lib/codegen/meta-python/base/__init__.py
Normal file
1
lib/codegen/meta-python/base/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Definitions for the base Cranelift language."""
|
||||
35
lib/codegen/meta-python/base/entities.py
Normal file
35
lib/codegen/meta-python/base/entities.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
The `cranelift.entities` module predefines all the Cranelift entity reference
|
||||
operand types. There are corresponding definitions in the `cranelift.entities`
|
||||
Rust module.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from cdsl.operands import EntityRefKind
|
||||
|
||||
|
||||
#: A reference to an extended basic block in the same function.
|
||||
#: This is primarliy used in control flow instructions.
|
||||
ebb = EntityRefKind(
|
||||
'ebb', 'An extended basic block in the same function.',
|
||||
default_member='destination')
|
||||
|
||||
#: A reference to a stack slot declared in the function preamble.
|
||||
stack_slot = EntityRefKind('stack_slot', 'A stack slot.')
|
||||
|
||||
#: A reference to a global value.
|
||||
global_value = EntityRefKind('global_value', 'A global value.')
|
||||
|
||||
#: A reference to a function sugnature declared in the function preamble.
|
||||
#: 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.
|
||||
#: This is used to provide the callee and signature in a call instruction.
|
||||
func_ref = EntityRefKind('func_ref', 'An external function.')
|
||||
|
||||
#: A reference to a jump table declared in the function preamble.
|
||||
jump_table = EntityRefKind(
|
||||
'jump_table', 'A jump table.', default_member='table')
|
||||
|
||||
#: A reference to a heap declared in the function preamble.
|
||||
heap = EntityRefKind('heap', 'A heap.')
|
||||
83
lib/codegen/meta-python/base/formats.py
Normal file
83
lib/codegen/meta-python/base/formats.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
The cranelift.formats defines all instruction formats.
|
||||
|
||||
Every instruction format has a corresponding `InstructionData` variant in the
|
||||
Rust representation of Cranelift IR, so all instruction formats must be defined
|
||||
in this module.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from cdsl.formats import InstructionFormat
|
||||
from cdsl.operands import VALUE, VARIABLE_ARGS
|
||||
from .immediates import imm64, uimm8, uimm32, ieee32, ieee64, offset32
|
||||
from .immediates import boolean, intcc, floatcc, memflags, regunit, trapcode
|
||||
from . import entities
|
||||
from .entities import ebb, sig_ref, func_ref, stack_slot, heap
|
||||
|
||||
Unary = InstructionFormat(VALUE)
|
||||
UnaryImm = InstructionFormat(imm64)
|
||||
UnaryIeee32 = InstructionFormat(ieee32)
|
||||
UnaryIeee64 = InstructionFormat(ieee64)
|
||||
UnaryBool = InstructionFormat(boolean)
|
||||
UnaryGlobalValue = InstructionFormat(entities.global_value)
|
||||
|
||||
Binary = InstructionFormat(VALUE, VALUE)
|
||||
BinaryImm = InstructionFormat(VALUE, imm64)
|
||||
|
||||
# The select instructions are controlled by the second VALUE operand.
|
||||
# The first VALUE operand is the controlling flag which has a derived type.
|
||||
# The fma instruction has the same constraint on all inputs.
|
||||
Ternary = InstructionFormat(VALUE, VALUE, VALUE, typevar_operand=1)
|
||||
|
||||
# Catch-all for instructions with many outputs and inputs and no immediate
|
||||
# operands.
|
||||
MultiAry = InstructionFormat(VARIABLE_ARGS)
|
||||
|
||||
NullAry = InstructionFormat()
|
||||
|
||||
InsertLane = InstructionFormat(VALUE, ('lane', uimm8), VALUE)
|
||||
ExtractLane = InstructionFormat(VALUE, ('lane', uimm8))
|
||||
|
||||
IntCompare = InstructionFormat(intcc, VALUE, VALUE)
|
||||
IntCompareImm = InstructionFormat(intcc, VALUE, imm64)
|
||||
IntCond = InstructionFormat(intcc, VALUE)
|
||||
FloatCompare = InstructionFormat(floatcc, VALUE, VALUE)
|
||||
FloatCond = InstructionFormat(floatcc, VALUE)
|
||||
|
||||
IntSelect = InstructionFormat(intcc, VALUE, VALUE, VALUE)
|
||||
|
||||
Jump = InstructionFormat(ebb, VARIABLE_ARGS)
|
||||
Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS)
|
||||
BranchInt = InstructionFormat(intcc, VALUE, ebb, VARIABLE_ARGS)
|
||||
BranchFloat = InstructionFormat(floatcc, VALUE, ebb, VARIABLE_ARGS)
|
||||
BranchIcmp = InstructionFormat(intcc, VALUE, VALUE, ebb, VARIABLE_ARGS)
|
||||
BranchTable = InstructionFormat(VALUE, entities.jump_table)
|
||||
|
||||
Call = InstructionFormat(func_ref, VARIABLE_ARGS)
|
||||
CallIndirect = InstructionFormat(sig_ref, VALUE, VARIABLE_ARGS)
|
||||
FuncAddr = InstructionFormat(func_ref)
|
||||
|
||||
Load = InstructionFormat(memflags, VALUE, offset32)
|
||||
LoadComplex = InstructionFormat(memflags, VARIABLE_ARGS, offset32)
|
||||
Store = InstructionFormat(memflags, VALUE, VALUE, offset32)
|
||||
StoreComplex = InstructionFormat(memflags, VALUE, VARIABLE_ARGS, offset32)
|
||||
|
||||
StackLoad = InstructionFormat(stack_slot, offset32)
|
||||
StackStore = InstructionFormat(VALUE, stack_slot, offset32)
|
||||
|
||||
# Accessing a WebAssembly heap.
|
||||
HeapAddr = InstructionFormat(heap, VALUE, uimm32)
|
||||
|
||||
RegMove = InstructionFormat(VALUE, ('src', regunit), ('dst', regunit))
|
||||
CopySpecial = InstructionFormat(('src', regunit), ('dst', regunit))
|
||||
RegSpill = InstructionFormat(
|
||||
VALUE, ('src', regunit), ('dst', entities.stack_slot))
|
||||
RegFill = InstructionFormat(
|
||||
VALUE, ('src', entities.stack_slot), ('dst', regunit))
|
||||
|
||||
Trap = InstructionFormat(trapcode)
|
||||
CondTrap = InstructionFormat(VALUE, trapcode)
|
||||
IntCondTrap = InstructionFormat(intcc, VALUE, trapcode)
|
||||
FloatCondTrap = InstructionFormat(floatcc, VALUE, trapcode)
|
||||
|
||||
# Finally extract the names of global values in this module.
|
||||
InstructionFormat.extract_names(globals())
|
||||
123
lib/codegen/meta-python/base/immediates.py
Normal file
123
lib/codegen/meta-python/base/immediates.py
Normal file
@@ -0,0 +1,123 @@
|
||||
"""
|
||||
The `cranelift.immediates` module predefines all the Cranelift immediate
|
||||
operand types.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from cdsl.operands import ImmediateKind
|
||||
|
||||
#: A 64-bit immediate integer operand.
|
||||
#:
|
||||
#: This type of immediate integer can interact with SSA values with any
|
||||
#: :py:class:`cranelift.IntType` type.
|
||||
imm64 = ImmediateKind('imm64', 'A 64-bit immediate integer.')
|
||||
|
||||
#: An unsigned 8-bit immediate integer operand.
|
||||
#:
|
||||
#: This small operand is used to indicate lane indexes in SIMD vectors and
|
||||
#: immediate bit counts on shift instructions.
|
||||
uimm8 = ImmediateKind('uimm8', 'An 8-bit immediate unsigned integer.')
|
||||
|
||||
#: An unsigned 32-bit immediate integer operand.
|
||||
uimm32 = ImmediateKind('uimm32', 'A 32-bit immediate unsigned integer.')
|
||||
|
||||
#: A 32-bit immediate signed offset.
|
||||
#:
|
||||
#: This is used to represent an immediate address offset in load/store
|
||||
#: instructions.
|
||||
offset32 = ImmediateKind(
|
||||
'offset32',
|
||||
'A 32-bit immediate signed offset.',
|
||||
default_member='offset')
|
||||
|
||||
#: A 32-bit immediate floating point operand.
|
||||
#:
|
||||
#: IEEE 754-2008 binary32 interchange format.
|
||||
ieee32 = ImmediateKind('ieee32', 'A 32-bit immediate floating point number.')
|
||||
|
||||
#: A 64-bit immediate floating point operand.
|
||||
#:
|
||||
#: IEEE 754-2008 binary64 interchange format.
|
||||
ieee64 = ImmediateKind('ieee64', 'A 64-bit immediate floating point number.')
|
||||
|
||||
#: An immediate boolean operand.
|
||||
#:
|
||||
#: This type of immediate boolean can interact with SSA values with any
|
||||
#: :py:class:`cranelift.BoolType` type.
|
||||
boolean = ImmediateKind('bool', 'An immediate boolean.',
|
||||
rust_type='bool')
|
||||
|
||||
#: A condition code for comparing integer values.
|
||||
#:
|
||||
#: This enumerated operand kind is used for the :clif:inst:`icmp` instruction
|
||||
#: and corresponds to the `condcodes::IntCC` Rust type.
|
||||
intcc = ImmediateKind(
|
||||
'intcc',
|
||||
'An integer comparison condition code.',
|
||||
default_member='cond',
|
||||
rust_type='ir::condcodes::IntCC',
|
||||
values={
|
||||
'eq': 'Equal',
|
||||
'ne': 'NotEqual',
|
||||
'sge': 'SignedGreaterThanOrEqual',
|
||||
'sgt': 'SignedGreaterThan',
|
||||
'sle': 'SignedLessThanOrEqual',
|
||||
'slt': 'SignedLessThan',
|
||||
'uge': 'UnsignedGreaterThanOrEqual',
|
||||
'ugt': 'UnsignedGreaterThan',
|
||||
'ule': 'UnsignedLessThanOrEqual',
|
||||
'ult': 'UnsignedLessThan',
|
||||
})
|
||||
|
||||
#: A condition code for comparing floating point values.
|
||||
#:
|
||||
#: This enumerated operand kind is used for the :clif:inst:`fcmp` instruction
|
||||
#: and corresponds to the `condcodes::FloatCC` Rust type.
|
||||
floatcc = ImmediateKind(
|
||||
'floatcc',
|
||||
'A floating point comparison condition code.',
|
||||
default_member='cond',
|
||||
rust_type='ir::condcodes::FloatCC',
|
||||
values={
|
||||
'ord': 'Ordered',
|
||||
'uno': 'Unordered',
|
||||
'eq': 'Equal',
|
||||
'ne': 'NotEqual',
|
||||
'one': 'OrderedNotEqual',
|
||||
'ueq': 'UnorderedOrEqual',
|
||||
'lt': 'LessThan',
|
||||
'le': 'LessThanOrEqual',
|
||||
'gt': 'GreaterThan',
|
||||
'ge': 'GreaterThanOrEqual',
|
||||
'ult': 'UnorderedOrLessThan',
|
||||
'ule': 'UnorderedOrLessThanOrEqual',
|
||||
'ugt': 'UnorderedOrGreaterThan',
|
||||
'uge': 'UnorderedOrGreaterThanOrEqual',
|
||||
})
|
||||
|
||||
#: Flags for memory operations like :clif:inst:`load` and :clif:inst:`store`.
|
||||
memflags = ImmediateKind(
|
||||
'memflags',
|
||||
'Memory operation flags',
|
||||
default_member='flags', rust_type='ir::MemFlags')
|
||||
|
||||
#: A register unit in the current target ISA.
|
||||
regunit = ImmediateKind(
|
||||
'regunit',
|
||||
'A register unit in the target ISA',
|
||||
rust_type='isa::RegUnit')
|
||||
|
||||
#: A trap code indicating the reason for trapping.
|
||||
#:
|
||||
#: The Rust enum type also has a `User(u16)` variant for user-provided trap
|
||||
#: codes.
|
||||
trapcode = ImmediateKind(
|
||||
'trapcode',
|
||||
'A trap reason code.',
|
||||
default_member='code',
|
||||
rust_type='ir::TrapCode',
|
||||
values={
|
||||
"stk_ovf": 'StackOverflow',
|
||||
"heap_oob": 'HeapOutOfBounds',
|
||||
"int_ovf": 'IntegerOverflow',
|
||||
"int_divz": 'IntegerDivisionByZero',
|
||||
})
|
||||
1925
lib/codegen/meta-python/base/instructions.py
Normal file
1925
lib/codegen/meta-python/base/instructions.py
Normal file
File diff suppressed because it is too large
Load Diff
414
lib/codegen/meta-python/base/legalize.py
Normal file
414
lib/codegen/meta-python/base/legalize.py
Normal file
@@ -0,0 +1,414 @@
|
||||
"""
|
||||
Patterns for legalizing the `base` instruction set.
|
||||
|
||||
The base Cranelift instruction set is 'fat', and many instructions don't have
|
||||
legal representations in a given target ISA. This module defines legalization
|
||||
patterns that describe how base instructions can be transformed to other base
|
||||
instructions that are legal.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from .immediates import intcc, imm64, ieee32, ieee64
|
||||
from . import instructions as insts
|
||||
from . import types
|
||||
from .instructions import uextend, sextend, ireduce
|
||||
from .instructions import iadd, iadd_cout, iadd_cin, iadd_carry, iadd_imm
|
||||
from .instructions import isub, isub_bin, isub_bout, isub_borrow, irsub_imm
|
||||
from .instructions import imul, imul_imm
|
||||
from .instructions import sdiv, sdiv_imm, udiv, udiv_imm
|
||||
from .instructions import srem, srem_imm, urem, urem_imm
|
||||
from .instructions import band, bor, bxor, isplit, iconcat
|
||||
from .instructions import bnot, band_not, bor_not, bxor_not
|
||||
from .instructions import band_imm, bor_imm, bxor_imm
|
||||
from .instructions import icmp, icmp_imm, ifcmp, ifcmp_imm
|
||||
from .instructions import iconst, bint, select
|
||||
from .instructions import ishl, ishl_imm, sshr, sshr_imm, ushr, ushr_imm
|
||||
from .instructions import rotl, rotl_imm, rotr, rotr_imm
|
||||
from .instructions import f32const, f64const
|
||||
from .instructions import store, load
|
||||
from .instructions import br_table
|
||||
from cdsl.ast import Var
|
||||
from cdsl.xform import Rtl, XFormGroup
|
||||
|
||||
|
||||
narrow = XFormGroup('narrow', """
|
||||
Legalize instructions by narrowing.
|
||||
|
||||
The transformations in the 'narrow' group work by expressing
|
||||
instructions in terms of smaller types. Operations on vector types are
|
||||
expressed in terms of vector types with fewer lanes, and integer
|
||||
operations are expressed in terms of smaller integer types.
|
||||
""")
|
||||
|
||||
widen = XFormGroup('widen', """
|
||||
Legalize instructions by widening.
|
||||
|
||||
The transformations in the 'widen' group work by expressing
|
||||
instructions in terms of larger types.
|
||||
""")
|
||||
|
||||
expand = XFormGroup('expand', """
|
||||
Legalize instructions by expansion.
|
||||
|
||||
Rewrite instructions in terms of other instructions, generally
|
||||
operating on the same types as the original instructions.
|
||||
""")
|
||||
|
||||
expand_flags = XFormGroup('expand_flags', """
|
||||
Instruction expansions for architectures with flags.
|
||||
|
||||
Expand some instructions using CPU flags, then fall back to the normal
|
||||
expansions. Not all architectures support CPU flags, so these patterns
|
||||
are kept separate.
|
||||
""", chain=expand)
|
||||
|
||||
|
||||
# Custom expansions for memory objects.
|
||||
expand.custom_legalize(insts.global_value, 'expand_global_value')
|
||||
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')
|
||||
expand.custom_legalize(insts.trapnz, 'expand_cond_trap')
|
||||
expand.custom_legalize(insts.br_table, 'expand_br_table')
|
||||
expand.custom_legalize(insts.select, 'expand_select')
|
||||
|
||||
# Custom expansions for floating point constants.
|
||||
# These expansions require bit-casting or creating constant pool entries.
|
||||
expand.custom_legalize(insts.f32const, 'expand_fconst')
|
||||
expand.custom_legalize(insts.f64const, 'expand_fconst')
|
||||
|
||||
# Custom expansions for stack memory accesses.
|
||||
expand.custom_legalize(insts.stack_load, 'expand_stack_load')
|
||||
expand.custom_legalize(insts.stack_store, 'expand_stack_store')
|
||||
|
||||
x = Var('x')
|
||||
y = Var('y')
|
||||
a = Var('a')
|
||||
a1 = Var('a1')
|
||||
a2 = Var('a2')
|
||||
b = Var('b')
|
||||
b1 = Var('b1')
|
||||
b2 = Var('b2')
|
||||
b_in = Var('b_in')
|
||||
b_int = Var('b_int')
|
||||
c = Var('c')
|
||||
c1 = Var('c1')
|
||||
c2 = Var('c2')
|
||||
c_in = Var('c_in')
|
||||
c_int = Var('c_int')
|
||||
d = Var('d')
|
||||
xl = Var('xl')
|
||||
xh = Var('xh')
|
||||
yl = Var('yl')
|
||||
yh = Var('yh')
|
||||
al = Var('al')
|
||||
ah = Var('ah')
|
||||
cc = Var('cc')
|
||||
ptr = Var('ptr')
|
||||
flags = Var('flags')
|
||||
offset = Var('off')
|
||||
ss = Var('ss')
|
||||
|
||||
narrow.legalize(
|
||||
a << iadd(x, y),
|
||||
Rtl(
|
||||
(xl, xh) << isplit(x),
|
||||
(yl, yh) << isplit(y),
|
||||
(al, c) << iadd_cout(xl, yl),
|
||||
ah << iadd_cin(xh, yh, c),
|
||||
a << iconcat(al, ah)
|
||||
))
|
||||
|
||||
narrow.legalize(
|
||||
a << isub(x, y),
|
||||
Rtl(
|
||||
(xl, xh) << isplit(x),
|
||||
(yl, yh) << isplit(y),
|
||||
(al, b) << isub_bout(xl, yl),
|
||||
ah << isub_bin(xh, yh, b),
|
||||
a << iconcat(al, ah)
|
||||
))
|
||||
|
||||
for bitop in [band, bor, bxor]:
|
||||
narrow.legalize(
|
||||
a << bitop(x, y),
|
||||
Rtl(
|
||||
(xl, xh) << isplit(x),
|
||||
(yl, yh) << isplit(y),
|
||||
al << bitop(xl, yl),
|
||||
ah << bitop(xh, yh),
|
||||
a << iconcat(al, ah)
|
||||
))
|
||||
|
||||
narrow.legalize(
|
||||
a << select(c, x, y),
|
||||
Rtl(
|
||||
(xl, xh) << isplit(x),
|
||||
(yl, yh) << isplit(y),
|
||||
al << select(c, xl, yl),
|
||||
ah << select(c, xh, yh),
|
||||
a << iconcat(al, ah)
|
||||
))
|
||||
|
||||
for int_ty in [types.i8, types.i16]:
|
||||
widen.legalize(
|
||||
a << iconst.bind(int_ty)(b),
|
||||
Rtl(
|
||||
c << iconst.i32(b),
|
||||
a << ireduce.bind(int_ty)(c)
|
||||
))
|
||||
|
||||
widen.legalize(
|
||||
store.i8(flags, a, ptr, offset),
|
||||
Rtl(
|
||||
b << uextend.i32(a),
|
||||
insts.istore8(flags, b, ptr, offset)
|
||||
))
|
||||
|
||||
widen.legalize(
|
||||
store.i16(flags, a, ptr, offset),
|
||||
Rtl(
|
||||
b << uextend.i32(a),
|
||||
insts.istore16(flags, b, ptr, offset)
|
||||
))
|
||||
|
||||
widen.legalize(
|
||||
a << load.i8(flags, ptr, offset),
|
||||
Rtl(
|
||||
b << insts.uload8.i32(flags, ptr, offset),
|
||||
a << ireduce(b)
|
||||
))
|
||||
|
||||
widen.legalize(
|
||||
a << load.i16(flags, ptr, offset),
|
||||
Rtl(
|
||||
b << insts.uload16.i32(flags, ptr, offset),
|
||||
a << ireduce(b)
|
||||
))
|
||||
|
||||
for binop in [iadd, isub, imul, udiv, band, bor, bxor]:
|
||||
for int_ty in [types.i8, types.i16]:
|
||||
widen.legalize(
|
||||
a << binop.bind(int_ty)(x, y),
|
||||
Rtl(
|
||||
b << uextend.i32(x),
|
||||
c << uextend.i32(y),
|
||||
d << binop(b, c),
|
||||
a << ireduce(d)
|
||||
)
|
||||
)
|
||||
|
||||
for binop in [sdiv]:
|
||||
for int_ty in [types.i8, types.i16]:
|
||||
widen.legalize(
|
||||
a << binop.bind(int_ty)(x, y),
|
||||
Rtl(
|
||||
b << sextend.i32(x),
|
||||
c << sextend.i32(y),
|
||||
d << binop(b, c),
|
||||
a << ireduce(d)
|
||||
)
|
||||
)
|
||||
|
||||
for unop in [bnot]:
|
||||
for int_ty in [types.i8, types.i16]:
|
||||
widen.legalize(
|
||||
a << unop.bind(int_ty)(x),
|
||||
Rtl(
|
||||
b << sextend.i32(x),
|
||||
d << unop(b),
|
||||
a << ireduce(d)
|
||||
)
|
||||
)
|
||||
|
||||
for binop in [iadd_imm, imul_imm, udiv_imm]:
|
||||
for int_ty in [types.i8, types.i16]:
|
||||
widen.legalize(
|
||||
a << binop.bind(int_ty)(x, y),
|
||||
Rtl(
|
||||
b << uextend.i32(x),
|
||||
c << binop(b, y),
|
||||
a << ireduce(c)
|
||||
)
|
||||
)
|
||||
|
||||
for binop in [sdiv_imm]:
|
||||
for int_ty in [types.i8, types.i16]:
|
||||
widen.legalize(
|
||||
a << binop.bind(int_ty)(x, y),
|
||||
Rtl(
|
||||
b << sextend.i32(x),
|
||||
c << binop(b, y),
|
||||
a << ireduce(c)
|
||||
)
|
||||
)
|
||||
|
||||
for int_ty in [types.i8, types.i16]:
|
||||
widen.legalize(
|
||||
br_table.bind(int_ty)(x, y),
|
||||
Rtl(
|
||||
b << uextend.i32(x),
|
||||
br_table(b, y),
|
||||
)
|
||||
)
|
||||
|
||||
# Expand integer operations with carry for RISC architectures that don't have
|
||||
# the flags.
|
||||
expand.legalize(
|
||||
(a, c) << iadd_cout(x, y),
|
||||
Rtl(
|
||||
a << iadd(x, y),
|
||||
c << icmp(intcc.ult, a, x)
|
||||
))
|
||||
|
||||
expand.legalize(
|
||||
(a, b) << isub_bout(x, y),
|
||||
Rtl(
|
||||
a << isub(x, y),
|
||||
b << icmp(intcc.ugt, a, x)
|
||||
))
|
||||
|
||||
expand.legalize(
|
||||
a << iadd_cin(x, y, c),
|
||||
Rtl(
|
||||
a1 << iadd(x, y),
|
||||
c_int << bint(c),
|
||||
a << iadd(a1, c_int)
|
||||
))
|
||||
|
||||
expand.legalize(
|
||||
a << isub_bin(x, y, b),
|
||||
Rtl(
|
||||
a1 << isub(x, y),
|
||||
b_int << bint(b),
|
||||
a << isub(a1, b_int)
|
||||
))
|
||||
|
||||
expand.legalize(
|
||||
(a, c) << iadd_carry(x, y, c_in),
|
||||
Rtl(
|
||||
(a1, c1) << iadd_cout(x, y),
|
||||
c_int << bint(c_in),
|
||||
(a, c2) << iadd_cout(a1, c_int),
|
||||
c << bor(c1, c2)
|
||||
))
|
||||
|
||||
expand.legalize(
|
||||
(a, b) << isub_borrow(x, y, b_in),
|
||||
Rtl(
|
||||
(a1, b1) << isub_bout(x, y),
|
||||
b_int << bint(b_in),
|
||||
(a, b2) << isub_bout(a1, b_int),
|
||||
b << bor(b1, b2)
|
||||
))
|
||||
|
||||
# Expansions for immediate operands that are out of range.
|
||||
for inst_imm, inst in [
|
||||
(iadd_imm, iadd),
|
||||
(imul_imm, imul),
|
||||
(sdiv_imm, sdiv),
|
||||
(udiv_imm, udiv),
|
||||
(srem_imm, srem),
|
||||
(urem_imm, urem),
|
||||
(band_imm, band),
|
||||
(bor_imm, bor),
|
||||
(bxor_imm, bor),
|
||||
(ifcmp_imm, ifcmp)]:
|
||||
expand.legalize(
|
||||
a << inst_imm(x, y),
|
||||
Rtl(
|
||||
a1 << iconst(y),
|
||||
a << inst(x, a1)
|
||||
))
|
||||
expand.legalize(
|
||||
a << irsub_imm(y, x),
|
||||
Rtl(
|
||||
a1 << iconst(x),
|
||||
a << isub(a1, y)
|
||||
))
|
||||
|
||||
# Rotates and shifts.
|
||||
for inst_imm, inst in [
|
||||
(rotl_imm, rotl),
|
||||
(rotr_imm, rotr),
|
||||
(ishl_imm, ishl),
|
||||
(sshr_imm, sshr),
|
||||
(ushr_imm, ushr)]:
|
||||
expand.legalize(
|
||||
a << inst_imm(x, y),
|
||||
Rtl(
|
||||
a1 << iconst.i32(y),
|
||||
a << inst(x, a1)
|
||||
))
|
||||
|
||||
expand.legalize(
|
||||
a << icmp_imm(cc, x, y),
|
||||
Rtl(
|
||||
a1 << iconst(y),
|
||||
a << icmp(cc, x, a1)
|
||||
))
|
||||
|
||||
# Expansions for *_not variants of bitwise ops.
|
||||
for inst_not, inst in [
|
||||
(band_not, band),
|
||||
(bor_not, bor),
|
||||
(bxor_not, bxor)]:
|
||||
expand.legalize(
|
||||
a << inst_not(x, y),
|
||||
Rtl(
|
||||
a1 << bnot(y),
|
||||
a << inst(x, a1)
|
||||
))
|
||||
|
||||
# Expand bnot using xor.
|
||||
expand.legalize(
|
||||
a << bnot(x),
|
||||
Rtl(
|
||||
y << iconst(imm64(-1)),
|
||||
a << bxor(x, y)
|
||||
))
|
||||
|
||||
# Floating-point sign manipulations.
|
||||
for ty, minus_zero in [
|
||||
(types.f32, f32const(ieee32.bits(0x80000000))),
|
||||
(types.f64, f64const(ieee64.bits(0x8000000000000000)))]:
|
||||
expand.legalize(
|
||||
a << insts.fabs.bind(ty)(x),
|
||||
Rtl(
|
||||
b << minus_zero,
|
||||
a << band_not(x, b),
|
||||
))
|
||||
expand.legalize(
|
||||
a << insts.fneg.bind(ty)(x),
|
||||
Rtl(
|
||||
b << minus_zero,
|
||||
a << bxor(x, b),
|
||||
))
|
||||
expand.legalize(
|
||||
a << insts.fcopysign.bind(ty)(x, y),
|
||||
Rtl(
|
||||
b << minus_zero,
|
||||
a1 << band_not(x, b),
|
||||
a2 << band(y, b),
|
||||
a << bor(a1, a2)
|
||||
))
|
||||
|
||||
|
||||
# Expansions using CPU flags.
|
||||
|
||||
expand_flags.legalize(
|
||||
insts.trapnz(x, c),
|
||||
Rtl(
|
||||
a << insts.ifcmp_imm(x, imm64(0)),
|
||||
insts.trapif(intcc.ne, a, c)
|
||||
))
|
||||
expand_flags.legalize(
|
||||
insts.trapz(x, c),
|
||||
Rtl(
|
||||
a << insts.ifcmp_imm(x, imm64(0)),
|
||||
insts.trapif(intcc.eq, a, c)
|
||||
))
|
||||
42
lib/codegen/meta-python/base/predicates.py
Normal file
42
lib/codegen/meta-python/base/predicates.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
Cranelift predicates that consider `Function` fields.
|
||||
"""
|
||||
from cdsl.predicates import FieldPredicate
|
||||
from .formats import UnaryGlobalValue, InstructionFormat
|
||||
|
||||
try:
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from cdsl.formats import InstructionFormat, 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__(
|
||||
UnaryGlobalValue.global_value, 'is_colocated_data', ('func',))
|
||||
|
||||
|
||||
class LengthEquals(FieldPredicate):
|
||||
def __init__(self, iform, num):
|
||||
# type: (InstructionFormat, int) -> None
|
||||
super(LengthEquals, self).__init__(
|
||||
iform.args(), 'has_length_of', (num, 'func'))
|
||||
218
lib/codegen/meta-python/base/semantics.py
Normal file
218
lib/codegen/meta-python/base/semantics.py
Normal file
@@ -0,0 +1,218 @@
|
||||
from __future__ import absolute_import
|
||||
from semantics.primitives import prim_to_bv, prim_from_bv, bvsplit, bvconcat,\
|
||||
bvadd, bvzeroext, bvsignext
|
||||
from semantics.primitives import bveq, bvne, bvsge, bvsgt, bvsle, bvslt,\
|
||||
bvuge, bvugt, bvule, bvult
|
||||
from semantics.macros import bool2bv
|
||||
from .instructions import vsplit, vconcat, iadd, iadd_cout, icmp, bextend, \
|
||||
isplit, iconcat, iadd_cin, iadd_carry
|
||||
from .immediates import intcc
|
||||
from cdsl.xform import Rtl, XForm
|
||||
from cdsl.ast import Var
|
||||
from cdsl.typevar import TypeSet
|
||||
from cdsl.ti import InTypeset
|
||||
|
||||
try:
|
||||
from typing import TYPE_CHECKING # noqa
|
||||
if TYPE_CHECKING:
|
||||
from cdsl.ast import Enumerator # noqa
|
||||
from cdsl.instructions import Instruction # noqa
|
||||
except ImportError:
|
||||
TYPE_CHECKING = False
|
||||
|
||||
x = Var('x')
|
||||
y = Var('y')
|
||||
a = Var('a')
|
||||
b = Var('b')
|
||||
c_out = Var('c_out')
|
||||
c_in = Var('c_in')
|
||||
CC = Var('CC')
|
||||
bc_out = Var('bc_out')
|
||||
bvc_out = Var('bvc_out')
|
||||
bvc_in = Var('bvc_in')
|
||||
xhi = Var('xhi')
|
||||
yhi = Var('yhi')
|
||||
ahi = Var('ahi')
|
||||
bhi = Var('bhi')
|
||||
xlo = Var('xlo')
|
||||
ylo = Var('ylo')
|
||||
alo = Var('alo')
|
||||
blo = Var('blo')
|
||||
lo = Var('lo')
|
||||
hi = Var('hi')
|
||||
bvx = Var('bvx')
|
||||
bvy = Var('bvy')
|
||||
bva = Var('bva')
|
||||
bvt = Var('bvt')
|
||||
bvs = Var('bvs')
|
||||
bva_wide = Var('bva_wide')
|
||||
bvlo = Var('bvlo')
|
||||
bvhi = Var('bvhi')
|
||||
|
||||
ScalarTS = TypeSet(lanes=(1, 1), ints=True, floats=True, bools=True)
|
||||
|
||||
vsplit.set_semantics(
|
||||
(lo, hi) << vsplit(x),
|
||||
Rtl(
|
||||
bvx << prim_to_bv(x),
|
||||
(bvlo, bvhi) << bvsplit(bvx),
|
||||
lo << prim_from_bv(bvlo),
|
||||
hi << prim_from_bv(bvhi)
|
||||
))
|
||||
|
||||
vconcat.set_semantics(
|
||||
x << vconcat(lo, hi),
|
||||
Rtl(
|
||||
bvlo << prim_to_bv(lo),
|
||||
bvhi << prim_to_bv(hi),
|
||||
bvx << bvconcat(bvlo, bvhi),
|
||||
x << prim_from_bv(bvx)
|
||||
))
|
||||
|
||||
iadd.set_semantics(
|
||||
a << iadd(x, y),
|
||||
(Rtl(
|
||||
bvx << prim_to_bv(x),
|
||||
bvy << prim_to_bv(y),
|
||||
bva << bvadd(bvx, bvy),
|
||||
a << prim_from_bv(bva)
|
||||
), [InTypeset(x.get_typevar(), ScalarTS)]),
|
||||
Rtl(
|
||||
(xlo, xhi) << vsplit(x),
|
||||
(ylo, yhi) << vsplit(y),
|
||||
alo << iadd(xlo, ylo),
|
||||
ahi << iadd(xhi, yhi),
|
||||
a << vconcat(alo, ahi)
|
||||
))
|
||||
|
||||
#
|
||||
# Integer arithmetic with carry and/or borrow.
|
||||
#
|
||||
iadd_cin.set_semantics(
|
||||
a << iadd_cin(x, y, c_in),
|
||||
Rtl(
|
||||
bvx << prim_to_bv(x),
|
||||
bvy << prim_to_bv(y),
|
||||
bvc_in << prim_to_bv(c_in),
|
||||
bvs << bvzeroext(bvc_in),
|
||||
bvt << bvadd(bvx, bvy),
|
||||
bva << bvadd(bvt, bvs),
|
||||
a << prim_from_bv(bva)
|
||||
))
|
||||
|
||||
iadd_cout.set_semantics(
|
||||
(a, c_out) << iadd_cout(x, y),
|
||||
Rtl(
|
||||
bvx << prim_to_bv(x),
|
||||
bvy << prim_to_bv(y),
|
||||
bva << bvadd(bvx, bvy),
|
||||
bc_out << bvult(bva, bvx),
|
||||
bvc_out << bool2bv(bc_out),
|
||||
a << prim_from_bv(bva),
|
||||
c_out << prim_from_bv(bvc_out)
|
||||
))
|
||||
|
||||
iadd_carry.set_semantics(
|
||||
(a, c_out) << iadd_carry(x, y, c_in),
|
||||
Rtl(
|
||||
bvx << prim_to_bv(x),
|
||||
bvy << prim_to_bv(y),
|
||||
bvc_in << prim_to_bv(c_in),
|
||||
bvs << bvzeroext(bvc_in),
|
||||
bvt << bvadd(bvx, bvy),
|
||||
bva << bvadd(bvt, bvs),
|
||||
bc_out << bvult(bva, bvx),
|
||||
bvc_out << bool2bv(bc_out),
|
||||
a << prim_from_bv(bva),
|
||||
c_out << prim_from_bv(bvc_out)
|
||||
))
|
||||
|
||||
bextend.set_semantics(
|
||||
a << bextend(x),
|
||||
(Rtl(
|
||||
bvx << prim_to_bv(x),
|
||||
bvy << bvsignext(bvx),
|
||||
a << prim_from_bv(bvy)
|
||||
), [InTypeset(x.get_typevar(), ScalarTS)]),
|
||||
Rtl(
|
||||
(xlo, xhi) << vsplit(x),
|
||||
alo << bextend(xlo),
|
||||
ahi << bextend(xhi),
|
||||
a << vconcat(alo, ahi)
|
||||
))
|
||||
|
||||
|
||||
def create_comp_xform(cc, bvcmp_func):
|
||||
# type: (Enumerator, Instruction) -> XForm
|
||||
ba = Var('ba')
|
||||
return XForm(
|
||||
Rtl(
|
||||
a << icmp(cc, x, y)
|
||||
),
|
||||
Rtl(
|
||||
bvx << prim_to_bv(x),
|
||||
bvy << prim_to_bv(y),
|
||||
ba << bvcmp_func(bvx, bvy),
|
||||
bva << bool2bv(ba),
|
||||
bva_wide << bvzeroext(bva),
|
||||
a << prim_from_bv(bva_wide),
|
||||
),
|
||||
constraints=InTypeset(x.get_typevar(), ScalarTS))
|
||||
|
||||
|
||||
icmp.set_semantics(
|
||||
a << icmp(CC, x, y),
|
||||
Rtl(
|
||||
(xlo, xhi) << vsplit(x),
|
||||
(ylo, yhi) << vsplit(y),
|
||||
alo << icmp(CC, xlo, ylo),
|
||||
ahi << icmp(CC, xhi, yhi),
|
||||
b << vconcat(alo, ahi),
|
||||
a << bextend(b)
|
||||
),
|
||||
create_comp_xform(intcc.eq, bveq),
|
||||
create_comp_xform(intcc.ne, bvne),
|
||||
create_comp_xform(intcc.sge, bvsge),
|
||||
create_comp_xform(intcc.sgt, bvsgt),
|
||||
create_comp_xform(intcc.sle, bvsle),
|
||||
create_comp_xform(intcc.slt, bvslt),
|
||||
create_comp_xform(intcc.uge, bvuge),
|
||||
create_comp_xform(intcc.ugt, bvugt),
|
||||
create_comp_xform(intcc.ule, bvule),
|
||||
create_comp_xform(intcc.ult, bvult))
|
||||
|
||||
#
|
||||
# Legalization helper instructions.
|
||||
#
|
||||
|
||||
isplit.set_semantics(
|
||||
(xlo, xhi) << isplit(x),
|
||||
(Rtl(
|
||||
bvx << prim_to_bv(x),
|
||||
(bvlo, bvhi) << bvsplit(bvx),
|
||||
xlo << prim_from_bv(bvlo),
|
||||
xhi << prim_from_bv(bvhi)
|
||||
), [InTypeset(x.get_typevar(), ScalarTS)]),
|
||||
Rtl(
|
||||
(a, b) << vsplit(x),
|
||||
(alo, ahi) << isplit(a),
|
||||
(blo, bhi) << isplit(b),
|
||||
xlo << vconcat(alo, blo),
|
||||
xhi << vconcat(bhi, bhi)
|
||||
))
|
||||
|
||||
iconcat.set_semantics(
|
||||
x << iconcat(xlo, xhi),
|
||||
(Rtl(
|
||||
bvlo << prim_to_bv(xlo),
|
||||
bvhi << prim_to_bv(xhi),
|
||||
bvx << bvconcat(bvlo, bvhi),
|
||||
x << prim_from_bv(bvx)
|
||||
), [InTypeset(x.get_typevar(), ScalarTS)]),
|
||||
Rtl(
|
||||
(alo, ahi) << vsplit(xlo),
|
||||
(blo, bhi) << vsplit(xhi),
|
||||
a << iconcat(alo, blo),
|
||||
b << iconcat(ahi, bhi),
|
||||
x << vconcat(a, b),
|
||||
))
|
||||
169
lib/codegen/meta-python/base/settings.py
Normal file
169
lib/codegen/meta-python/base/settings.py
Normal file
@@ -0,0 +1,169 @@
|
||||
"""
|
||||
Cranelift shared settings.
|
||||
|
||||
This module defines settings relevant for all code generators.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from cdsl.settings import SettingGroup, BoolSetting, EnumSetting, NumSetting
|
||||
|
||||
group = SettingGroup('shared')
|
||||
|
||||
opt_level = EnumSetting(
|
||||
"""
|
||||
Optimization level:
|
||||
|
||||
- default: Very profitable optimizations enabled, none slow.
|
||||
- best: Enable all optimizations
|
||||
- fastest: Optimize for compile time by disabling most optimizations.
|
||||
""",
|
||||
'default', 'best', 'fastest')
|
||||
|
||||
enable_verifier = BoolSetting(
|
||||
"""
|
||||
Run the Cranelift IR verifier at strategic times during compilation.
|
||||
|
||||
This makes compilation slower but catches many bugs. The verifier is
|
||||
disabled by default, except when reading Cranelift IR from a text file.
|
||||
""",
|
||||
default=True)
|
||||
|
||||
call_conv = EnumSetting(
|
||||
"""
|
||||
Default calling convention:
|
||||
|
||||
- fast: not-ABI-stable convention for best performance
|
||||
- cold: not-ABI-stable convention for infrequently executed code
|
||||
- system_v: System V-style convention used on many platforms
|
||||
- windows_fastcall: Windows "fastcall" convention, also used for
|
||||
x64 and ARM
|
||||
- baldrdash: SpiderMonkey WebAssembly convention
|
||||
- probestack: specialized convention for the probestack function
|
||||
|
||||
The default calling convention may be overridden by individual
|
||||
functions.
|
||||
""",
|
||||
|
||||
'fast',
|
||||
'cold',
|
||||
'system_v',
|
||||
'windows_fastcall',
|
||||
'baldrdash',
|
||||
'probestack'
|
||||
)
|
||||
|
||||
# Note that Cranelift 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 values.
|
||||
is_pic = BoolSetting("Enable Position-Independent Code generation")
|
||||
|
||||
colocated_libcalls = BoolSetting(
|
||||
"""
|
||||
Use colocated libcalls.
|
||||
|
||||
Generate code that assumes that libcalls can be declared "colocated",
|
||||
meaning they will be defined along with the current function, such that
|
||||
they can use more efficient addressing.
|
||||
""")
|
||||
|
||||
return_at_end = BoolSetting(
|
||||
"""
|
||||
Generate functions with at most a single return instruction at the
|
||||
end of the function.
|
||||
|
||||
This guarantees that functions do not have any internal return
|
||||
instructions. Either they never return, or they have a single return
|
||||
instruction at the end.
|
||||
""")
|
||||
|
||||
avoid_div_traps = BoolSetting(
|
||||
"""
|
||||
Generate explicit checks around native division instructions to avoid
|
||||
their trapping.
|
||||
|
||||
This is primarily used by SpiderMonkey which doesn't install a signal
|
||||
handler for SIGFPE, but expects a SIGILL trap for division by zero.
|
||||
|
||||
On ISAs like ARM where the native division instructions don't trap,
|
||||
this setting has no effect - explicit checks are always inserted.
|
||||
""")
|
||||
|
||||
enable_float = BoolSetting(
|
||||
"""
|
||||
Enable the use of floating-point instructions
|
||||
|
||||
Disabling use of floating-point instructions is not yet implemented.
|
||||
""",
|
||||
default=True)
|
||||
|
||||
enable_nan_canonicalization = BoolSetting(
|
||||
"""
|
||||
Enable NaN canonicalization
|
||||
|
||||
This replaces NaNs with a single canonical value, for users requiring
|
||||
entirely deterministic WebAssembly computation. This is not required
|
||||
by the WebAssembly spec, so it is not enabled by default.
|
||||
""",
|
||||
default=False)
|
||||
|
||||
enable_simd = BoolSetting(
|
||||
"""Enable the use of SIMD instructions.""",
|
||||
default=True)
|
||||
|
||||
enable_atomics = BoolSetting(
|
||||
"""Enable the use of atomic instructions""",
|
||||
default=True)
|
||||
|
||||
#
|
||||
# Settings specific to the `baldrdash` calling convention.
|
||||
#
|
||||
baldrdash_prologue_words = NumSetting(
|
||||
"""
|
||||
Number of pointer-sized words pushed by the baldrdash prologue.
|
||||
|
||||
Functions with the `baldrdash` calling convention don't generate their
|
||||
own prologue and epilogue. They depend on externally generated code
|
||||
that pushes a fixed number of words in the prologue and restores them
|
||||
in the epilogue.
|
||||
|
||||
This setting configures the number of pointer-sized words pushed on the
|
||||
stack when the Cranelift-generated code is entered. This includes the
|
||||
pushed return address on x86.
|
||||
""")
|
||||
|
||||
#
|
||||
# BaldrMonkey requires that not-yet-relocated function addresses be encoded
|
||||
# as all-ones bitpatterns.
|
||||
#
|
||||
allones_funcaddrs = BoolSetting(
|
||||
"""
|
||||
Emit not-yet-relocated function addresses as all-ones bit patterns.
|
||||
""")
|
||||
|
||||
#
|
||||
# Stack probing options.
|
||||
#
|
||||
probestack_enabled = BoolSetting(
|
||||
"""
|
||||
Enable the use of stack probes, for calling conventions which support
|
||||
this functionality.
|
||||
""",
|
||||
default=True)
|
||||
|
||||
probestack_func_adjusts_sp = BoolSetting(
|
||||
"""
|
||||
Set this to true of the stack probe function modifies the stack pointer
|
||||
itself.
|
||||
""")
|
||||
|
||||
probestack_size_log2 = NumSetting(
|
||||
"""
|
||||
The log2 of the size of the stack guard region.
|
||||
|
||||
Stack frames larger than this size will have stack overflow checked
|
||||
by calling the probestack function.
|
||||
|
||||
The default is 12, which translates to a size of 4096.
|
||||
""",
|
||||
default=12)
|
||||
|
||||
group.close(globals())
|
||||
46
lib/codegen/meta-python/base/types.py
Normal file
46
lib/codegen/meta-python/base/types.py
Normal file
@@ -0,0 +1,46 @@
|
||||
"""
|
||||
The base.types module predefines all the Cranelift scalar types.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from cdsl.types import IntType, FloatType, BoolType, FlagsType
|
||||
|
||||
#: Boolean.
|
||||
b1 = BoolType(1) #: 1-bit bool. Type is abstract (can't be stored in mem)
|
||||
b8 = BoolType(8) #: 8-bit bool.
|
||||
b16 = BoolType(16) #: 16-bit bool.
|
||||
b32 = BoolType(32) #: 32-bit bool.
|
||||
b64 = BoolType(64) #: 64-bit bool.
|
||||
|
||||
i8 = IntType(8) #: 8-bit int.
|
||||
i16 = IntType(16) #: 16-bit int.
|
||||
i32 = IntType(32) #: 32-bit int.
|
||||
i64 = IntType(64) #: 64-bit int.
|
||||
|
||||
#: IEEE single precision.
|
||||
f32 = FloatType(
|
||||
32, """
|
||||
A 32-bit floating point type represented in the IEEE 754-2008
|
||||
*binary32* interchange format. This corresponds to the :c:type:`float`
|
||||
type in most C implementations.
|
||||
""")
|
||||
|
||||
#: IEEE double precision.
|
||||
f64 = FloatType(
|
||||
64, """
|
||||
A 64-bit floating point type represented in the IEEE 754-2008
|
||||
*binary64* interchange format. This corresponds to the :c:type:`double`
|
||||
type in most C implementations.
|
||||
""")
|
||||
#: CPU flags from an integer comparison.
|
||||
iflags = FlagsType(
|
||||
'iflags', """
|
||||
CPU flags representing the result of an integer comparison. These flags
|
||||
can be tested with an :type:`intcc` condition code.
|
||||
""")
|
||||
|
||||
#: CPU flags from a floating point comparison.
|
||||
fflags = FlagsType(
|
||||
'fflags', """
|
||||
CPU flags representing the result of a floating point comparison. These
|
||||
flags can be tested with a :type:`floatcc` condition code.
|
||||
""")
|
||||
Reference in New Issue
Block a user