init
This commit is contained in:
commit
38355d2442
9083 changed files with 1225834 additions and 0 deletions
618
.venv/lib/python3.8/site-packages/mypyc/codegen/emitfunc.py
Normal file
618
.venv/lib/python3.8/site-packages/mypyc/codegen/emitfunc.py
Normal file
|
|
@ -0,0 +1,618 @@
|
|||
"""Code generation for native function bodies."""
|
||||
|
||||
from typing import Union, Optional
|
||||
from typing_extensions import Final
|
||||
|
||||
from mypyc.common import (
|
||||
REG_PREFIX, NATIVE_PREFIX, STATIC_PREFIX, TYPE_PREFIX, MODULE_PREFIX,
|
||||
)
|
||||
from mypyc.codegen.emit import Emitter
|
||||
from mypyc.ir.ops import (
|
||||
OpVisitor, Goto, Branch, Return, Assign, Integer, LoadErrorValue, GetAttr, SetAttr,
|
||||
LoadStatic, InitStatic, TupleGet, TupleSet, Call, IncRef, DecRef, Box, Cast, Unbox,
|
||||
BasicBlock, Value, MethodCall, Unreachable, NAMESPACE_STATIC, NAMESPACE_TYPE, NAMESPACE_MODULE,
|
||||
RaiseStandardError, CallC, LoadGlobal, Truncate, IntOp, LoadMem, GetElementPtr,
|
||||
LoadAddress, ComparisonOp, SetMem, Register, LoadLiteral, AssignMulti, KeepAlive
|
||||
)
|
||||
from mypyc.ir.rtypes import (
|
||||
RType, RTuple, RArray, is_tagged, is_int32_rprimitive, is_int64_rprimitive, RStruct,
|
||||
is_pointer_rprimitive, is_int_rprimitive
|
||||
)
|
||||
from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD, all_values
|
||||
from mypyc.ir.class_ir import ClassIR
|
||||
from mypyc.ir.pprint import generate_names_for_ir
|
||||
from mypyc.analysis.blockfreq import frequently_executed_blocks
|
||||
|
||||
# Whether to insert debug asserts for all error handling, to quickly
|
||||
# catch errors propagating without exceptions set.
|
||||
DEBUG_ERRORS = False
|
||||
|
||||
|
||||
def native_function_type(fn: FuncIR, emitter: Emitter) -> str:
|
||||
args = ', '.join(emitter.ctype(arg.type) for arg in fn.args) or 'void'
|
||||
ret = emitter.ctype(fn.ret_type)
|
||||
return '{} (*)({})'.format(ret, args)
|
||||
|
||||
|
||||
def native_function_header(fn: FuncDecl, emitter: Emitter) -> str:
|
||||
args = []
|
||||
for arg in fn.sig.args:
|
||||
args.append('{}{}{}'.format(emitter.ctype_spaced(arg.type), REG_PREFIX, arg.name))
|
||||
|
||||
return '{ret_type}{name}({args})'.format(
|
||||
ret_type=emitter.ctype_spaced(fn.sig.ret_type),
|
||||
name=emitter.native_function_name(fn),
|
||||
args=', '.join(args) or 'void')
|
||||
|
||||
|
||||
def generate_native_function(fn: FuncIR,
|
||||
emitter: Emitter,
|
||||
source_path: str,
|
||||
module_name: str) -> None:
|
||||
declarations = Emitter(emitter.context)
|
||||
names = generate_names_for_ir(fn.arg_regs, fn.blocks)
|
||||
body = Emitter(emitter.context, names)
|
||||
visitor = FunctionEmitterVisitor(body, declarations, source_path, module_name)
|
||||
|
||||
declarations.emit_line('{} {{'.format(native_function_header(fn.decl, emitter)))
|
||||
body.indent()
|
||||
|
||||
for r in all_values(fn.arg_regs, fn.blocks):
|
||||
if isinstance(r.type, RTuple):
|
||||
emitter.declare_tuple_struct(r.type)
|
||||
if isinstance(r.type, RArray):
|
||||
continue # Special: declared on first assignment
|
||||
|
||||
if r in fn.arg_regs:
|
||||
continue # Skip the arguments
|
||||
|
||||
ctype = emitter.ctype_spaced(r.type)
|
||||
init = ''
|
||||
declarations.emit_line('{ctype}{prefix}{name}{init};'.format(ctype=ctype,
|
||||
prefix=REG_PREFIX,
|
||||
name=names[r],
|
||||
init=init))
|
||||
|
||||
# Before we emit the blocks, give them all labels
|
||||
blocks = fn.blocks
|
||||
for i, block in enumerate(blocks):
|
||||
block.label = i
|
||||
|
||||
common = frequently_executed_blocks(fn.blocks[0])
|
||||
|
||||
for i in range(len(blocks)):
|
||||
block = blocks[i]
|
||||
visitor.rare = block not in common
|
||||
next_block = None
|
||||
if i + 1 < len(blocks):
|
||||
next_block = blocks[i + 1]
|
||||
body.emit_label(block)
|
||||
visitor.next_block = next_block
|
||||
for op in block.ops:
|
||||
op.accept(visitor)
|
||||
|
||||
body.emit_line('}')
|
||||
|
||||
emitter.emit_from_emitter(declarations)
|
||||
emitter.emit_from_emitter(body)
|
||||
|
||||
|
||||
class FunctionEmitterVisitor(OpVisitor[None]):
|
||||
def __init__(self,
|
||||
emitter: Emitter,
|
||||
declarations: Emitter,
|
||||
source_path: str,
|
||||
module_name: str) -> None:
|
||||
self.emitter = emitter
|
||||
self.names = emitter.names
|
||||
self.declarations = declarations
|
||||
self.source_path = source_path
|
||||
self.module_name = module_name
|
||||
self.literals = emitter.context.literals
|
||||
self.rare = False
|
||||
self.next_block: Optional[BasicBlock] = None
|
||||
|
||||
def temp_name(self) -> str:
|
||||
return self.emitter.temp_name()
|
||||
|
||||
def visit_goto(self, op: Goto) -> None:
|
||||
if op.label is not self.next_block:
|
||||
self.emit_line('goto %s;' % self.label(op.label))
|
||||
|
||||
def visit_branch(self, op: Branch) -> None:
|
||||
true, false = op.true, op.false
|
||||
negated = op.negated
|
||||
negated_rare = False
|
||||
if true is self.next_block and op.traceback_entry is None:
|
||||
# Switch true/false since it avoids an else block.
|
||||
true, false = false, true
|
||||
negated = not negated
|
||||
negated_rare = True
|
||||
|
||||
neg = '!' if negated else ''
|
||||
cond = ''
|
||||
if op.op == Branch.BOOL:
|
||||
expr_result = self.reg(op.value)
|
||||
cond = '{}{}'.format(neg, expr_result)
|
||||
elif op.op == Branch.IS_ERROR:
|
||||
typ = op.value.type
|
||||
compare = '!=' if negated else '=='
|
||||
if isinstance(typ, RTuple):
|
||||
# TODO: What about empty tuple?
|
||||
cond = self.emitter.tuple_undefined_check_cond(typ,
|
||||
self.reg(op.value),
|
||||
self.c_error_value,
|
||||
compare)
|
||||
else:
|
||||
cond = '{} {} {}'.format(self.reg(op.value),
|
||||
compare,
|
||||
self.c_error_value(typ))
|
||||
else:
|
||||
assert False, "Invalid branch"
|
||||
|
||||
# For error checks, tell the compiler the branch is unlikely
|
||||
if op.traceback_entry is not None or op.rare:
|
||||
if not negated_rare:
|
||||
cond = 'unlikely({})'.format(cond)
|
||||
else:
|
||||
cond = 'likely({})'.format(cond)
|
||||
|
||||
if false is self.next_block:
|
||||
if op.traceback_entry is None:
|
||||
self.emit_line('if ({}) goto {};'.format(cond, self.label(true)))
|
||||
else:
|
||||
self.emit_line('if ({}) {{'.format(cond))
|
||||
self.emit_traceback(op)
|
||||
self.emit_lines(
|
||||
'goto %s;' % self.label(true),
|
||||
'}'
|
||||
)
|
||||
else:
|
||||
self.emit_line('if ({}) {{'.format(cond))
|
||||
self.emit_traceback(op)
|
||||
self.emit_lines(
|
||||
'goto %s;' % self.label(true),
|
||||
'} else',
|
||||
' goto %s;' % self.label(false)
|
||||
)
|
||||
|
||||
def visit_return(self, op: Return) -> None:
|
||||
value_str = self.reg(op.value)
|
||||
self.emit_line('return %s;' % value_str)
|
||||
|
||||
def visit_tuple_set(self, op: TupleSet) -> None:
|
||||
dest = self.reg(op)
|
||||
tuple_type = op.tuple_type
|
||||
self.emitter.declare_tuple_struct(tuple_type)
|
||||
if len(op.items) == 0: # empty tuple
|
||||
self.emit_line('{}.empty_struct_error_flag = 0;'.format(dest))
|
||||
else:
|
||||
for i, item in enumerate(op.items):
|
||||
self.emit_line('{}.f{} = {};'.format(dest, i, self.reg(item)))
|
||||
self.emit_inc_ref(dest, tuple_type)
|
||||
|
||||
def visit_assign(self, op: Assign) -> None:
|
||||
dest = self.reg(op.dest)
|
||||
src = self.reg(op.src)
|
||||
# clang whines about self assignment (which we might generate
|
||||
# for some casts), so don't emit it.
|
||||
if dest != src:
|
||||
self.emit_line('%s = %s;' % (dest, src))
|
||||
|
||||
def visit_assign_multi(self, op: AssignMulti) -> None:
|
||||
typ = op.dest.type
|
||||
assert isinstance(typ, RArray)
|
||||
dest = self.reg(op.dest)
|
||||
# RArray values can only be assigned to once, so we can always
|
||||
# declare them on initialization.
|
||||
self.emit_line('%s%s[%d] = {%s};' % (
|
||||
self.emitter.ctype_spaced(typ.item_type),
|
||||
dest,
|
||||
len(op.src),
|
||||
', '.join(self.reg(s) for s in op.src)))
|
||||
|
||||
def visit_load_error_value(self, op: LoadErrorValue) -> None:
|
||||
if isinstance(op.type, RTuple):
|
||||
values = [self.c_undefined_value(item) for item in op.type.types]
|
||||
tmp = self.temp_name()
|
||||
self.emit_line('%s %s = { %s };' % (self.ctype(op.type), tmp, ', '.join(values)))
|
||||
self.emit_line('%s = %s;' % (self.reg(op), tmp))
|
||||
else:
|
||||
self.emit_line('%s = %s;' % (self.reg(op),
|
||||
self.c_error_value(op.type)))
|
||||
|
||||
def visit_load_literal(self, op: LoadLiteral) -> None:
|
||||
index = self.literals.literal_index(op.value)
|
||||
s = repr(op.value)
|
||||
if not any(x in s for x in ('/*', '*/', '\0')):
|
||||
ann = ' /* %s */' % s
|
||||
else:
|
||||
ann = ''
|
||||
if not is_int_rprimitive(op.type):
|
||||
self.emit_line('%s = CPyStatics[%d];%s' % (self.reg(op), index, ann))
|
||||
else:
|
||||
self.emit_line('%s = (CPyTagged)CPyStatics[%d] | 1;%s' % (
|
||||
self.reg(op), index, ann))
|
||||
|
||||
def get_attr_expr(self, obj: str, op: Union[GetAttr, SetAttr], decl_cl: ClassIR) -> str:
|
||||
"""Generate attribute accessor for normal (non-property) access.
|
||||
|
||||
This either has a form like obj->attr_name for attributes defined in non-trait
|
||||
classes, and *(obj + attr_offset) for attributes defined by traits. We also
|
||||
insert all necessary C casts here.
|
||||
"""
|
||||
cast = '({} *)'.format(op.class_type.struct_name(self.emitter.names))
|
||||
if decl_cl.is_trait and op.class_type.class_ir.is_trait:
|
||||
# For pure trait access find the offset first, offsets
|
||||
# are ordered by attribute position in the cl.attributes dict.
|
||||
# TODO: pre-calculate the mapping to make this faster.
|
||||
trait_attr_index = list(decl_cl.attributes).index(op.attr)
|
||||
# TODO: reuse these names somehow?
|
||||
offset = self.emitter.temp_name()
|
||||
self.declarations.emit_line('size_t {};'.format(offset))
|
||||
self.emitter.emit_line('{} = {};'.format(
|
||||
offset,
|
||||
'CPy_FindAttrOffset({}, {}, {})'.format(
|
||||
self.emitter.type_struct_name(decl_cl),
|
||||
'({}{})->vtable'.format(cast, obj),
|
||||
trait_attr_index,
|
||||
)
|
||||
))
|
||||
attr_cast = '({} *)'.format(self.ctype(op.class_type.attr_type(op.attr)))
|
||||
return '*{}((char *){} + {})'.format(attr_cast, obj, offset)
|
||||
else:
|
||||
# Cast to something non-trait. Note: for this to work, all struct
|
||||
# members for non-trait classes must obey monotonic linear growth.
|
||||
if op.class_type.class_ir.is_trait:
|
||||
assert not decl_cl.is_trait
|
||||
cast = '({} *)'.format(decl_cl.struct_name(self.emitter.names))
|
||||
return '({}{})->{}'.format(
|
||||
cast, obj, self.emitter.attr(op.attr)
|
||||
)
|
||||
|
||||
def visit_get_attr(self, op: GetAttr) -> None:
|
||||
dest = self.reg(op)
|
||||
obj = self.reg(op.obj)
|
||||
rtype = op.class_type
|
||||
cl = rtype.class_ir
|
||||
attr_rtype, decl_cl = cl.attr_details(op.attr)
|
||||
if cl.get_method(op.attr):
|
||||
# Properties are essentially methods, so use vtable access for them.
|
||||
version = '_TRAIT' if cl.is_trait else ''
|
||||
self.emit_line('%s = CPY_GET_ATTR%s(%s, %s, %d, %s, %s); /* %s */' % (
|
||||
dest,
|
||||
version,
|
||||
obj,
|
||||
self.emitter.type_struct_name(rtype.class_ir),
|
||||
rtype.getter_index(op.attr),
|
||||
rtype.struct_name(self.names),
|
||||
self.ctype(rtype.attr_type(op.attr)),
|
||||
op.attr))
|
||||
else:
|
||||
# Otherwise, use direct or offset struct access.
|
||||
attr_expr = self.get_attr_expr(obj, op, decl_cl)
|
||||
self.emitter.emit_line('{} = {};'.format(dest, attr_expr))
|
||||
self.emitter.emit_undefined_attr_check(
|
||||
attr_rtype, attr_expr, '==', unlikely=True
|
||||
)
|
||||
exc_class = 'PyExc_AttributeError'
|
||||
self.emitter.emit_line(
|
||||
'PyErr_SetString({}, "attribute {} of {} undefined");'.format(
|
||||
exc_class, repr(op.attr), repr(cl.name)))
|
||||
if attr_rtype.is_refcounted:
|
||||
self.emitter.emit_line('} else {')
|
||||
self.emitter.emit_inc_ref(attr_expr, attr_rtype)
|
||||
self.emitter.emit_line('}')
|
||||
|
||||
def visit_set_attr(self, op: SetAttr) -> None:
|
||||
dest = self.reg(op)
|
||||
obj = self.reg(op.obj)
|
||||
src = self.reg(op.src)
|
||||
rtype = op.class_type
|
||||
cl = rtype.class_ir
|
||||
attr_rtype, decl_cl = cl.attr_details(op.attr)
|
||||
if cl.get_method(op.attr):
|
||||
# Again, use vtable access for properties...
|
||||
version = '_TRAIT' if cl.is_trait else ''
|
||||
self.emit_line('%s = CPY_SET_ATTR%s(%s, %s, %d, %s, %s, %s); /* %s */' % (
|
||||
dest,
|
||||
version,
|
||||
obj,
|
||||
self.emitter.type_struct_name(rtype.class_ir),
|
||||
rtype.setter_index(op.attr),
|
||||
src,
|
||||
rtype.struct_name(self.names),
|
||||
self.ctype(rtype.attr_type(op.attr)),
|
||||
op.attr))
|
||||
else:
|
||||
# ...and struct access for normal attributes.
|
||||
attr_expr = self.get_attr_expr(obj, op, decl_cl)
|
||||
if attr_rtype.is_refcounted:
|
||||
self.emitter.emit_undefined_attr_check(attr_rtype, attr_expr, '!=')
|
||||
self.emitter.emit_dec_ref(attr_expr, attr_rtype)
|
||||
self.emitter.emit_line('}')
|
||||
# This steal the reference to src, so we don't need to increment the arg
|
||||
self.emitter.emit_lines(
|
||||
'{} = {};'.format(attr_expr, src),
|
||||
'{} = 1;'.format(dest),
|
||||
)
|
||||
|
||||
PREFIX_MAP: Final = {
|
||||
NAMESPACE_STATIC: STATIC_PREFIX,
|
||||
NAMESPACE_TYPE: TYPE_PREFIX,
|
||||
NAMESPACE_MODULE: MODULE_PREFIX,
|
||||
}
|
||||
|
||||
def visit_load_static(self, op: LoadStatic) -> None:
|
||||
dest = self.reg(op)
|
||||
prefix = self.PREFIX_MAP[op.namespace]
|
||||
name = self.emitter.static_name(op.identifier, op.module_name, prefix)
|
||||
if op.namespace == NAMESPACE_TYPE:
|
||||
name = '(PyObject *)%s' % name
|
||||
ann = ''
|
||||
if op.ann:
|
||||
s = repr(op.ann)
|
||||
if not any(x in s for x in ('/*', '*/', '\0')):
|
||||
ann = ' /* %s */' % s
|
||||
self.emit_line('%s = %s;%s' % (dest, name, ann))
|
||||
|
||||
def visit_init_static(self, op: InitStatic) -> None:
|
||||
value = self.reg(op.value)
|
||||
prefix = self.PREFIX_MAP[op.namespace]
|
||||
name = self.emitter.static_name(op.identifier, op.module_name, prefix)
|
||||
if op.namespace == NAMESPACE_TYPE:
|
||||
value = '(PyTypeObject *)%s' % value
|
||||
self.emit_line('%s = %s;' % (name, value))
|
||||
self.emit_inc_ref(name, op.value.type)
|
||||
|
||||
def visit_tuple_get(self, op: TupleGet) -> None:
|
||||
dest = self.reg(op)
|
||||
src = self.reg(op.src)
|
||||
self.emit_line('{} = {}.f{};'.format(dest, src, op.index))
|
||||
self.emit_inc_ref(dest, op.type)
|
||||
|
||||
def get_dest_assign(self, dest: Value) -> str:
|
||||
if not dest.is_void:
|
||||
return self.reg(dest) + ' = '
|
||||
else:
|
||||
return ''
|
||||
|
||||
def visit_call(self, op: Call) -> None:
|
||||
"""Call native function."""
|
||||
dest = self.get_dest_assign(op)
|
||||
args = ', '.join(self.reg(arg) for arg in op.args)
|
||||
lib = self.emitter.get_group_prefix(op.fn)
|
||||
cname = op.fn.cname(self.names)
|
||||
self.emit_line('%s%s%s%s(%s);' % (dest, lib, NATIVE_PREFIX, cname, args))
|
||||
|
||||
def visit_method_call(self, op: MethodCall) -> None:
|
||||
"""Call native method."""
|
||||
dest = self.get_dest_assign(op)
|
||||
obj = self.reg(op.obj)
|
||||
|
||||
rtype = op.receiver_type
|
||||
class_ir = rtype.class_ir
|
||||
name = op.method
|
||||
method = rtype.class_ir.get_method(name)
|
||||
assert method is not None
|
||||
|
||||
# Can we call the method directly, bypassing vtable?
|
||||
is_direct = class_ir.is_method_final(name)
|
||||
|
||||
# The first argument gets omitted for static methods and
|
||||
# turned into the class for class methods
|
||||
obj_args = (
|
||||
[] if method.decl.kind == FUNC_STATICMETHOD else
|
||||
['(PyObject *)Py_TYPE({})'.format(obj)] if method.decl.kind == FUNC_CLASSMETHOD else
|
||||
[obj])
|
||||
args = ', '.join(obj_args + [self.reg(arg) for arg in op.args])
|
||||
mtype = native_function_type(method, self.emitter)
|
||||
version = '_TRAIT' if rtype.class_ir.is_trait else ''
|
||||
if is_direct:
|
||||
# Directly call method, without going through the vtable.
|
||||
lib = self.emitter.get_group_prefix(method.decl)
|
||||
self.emit_line('{}{}{}{}({});'.format(
|
||||
dest, lib, NATIVE_PREFIX, method.cname(self.names), args))
|
||||
else:
|
||||
# Call using vtable.
|
||||
method_idx = rtype.method_index(name)
|
||||
self.emit_line('{}CPY_GET_METHOD{}({}, {}, {}, {}, {})({}); /* {} */'.format(
|
||||
dest, version, obj, self.emitter.type_struct_name(rtype.class_ir),
|
||||
method_idx, rtype.struct_name(self.names), mtype, args, op.method))
|
||||
|
||||
def visit_inc_ref(self, op: IncRef) -> None:
|
||||
src = self.reg(op.src)
|
||||
self.emit_inc_ref(src, op.src.type)
|
||||
|
||||
def visit_dec_ref(self, op: DecRef) -> None:
|
||||
src = self.reg(op.src)
|
||||
self.emit_dec_ref(src, op.src.type, is_xdec=op.is_xdec)
|
||||
|
||||
def visit_box(self, op: Box) -> None:
|
||||
self.emitter.emit_box(self.reg(op.src), self.reg(op), op.src.type, can_borrow=True)
|
||||
|
||||
def visit_cast(self, op: Cast) -> None:
|
||||
self.emitter.emit_cast(self.reg(op.src), self.reg(op), op.type,
|
||||
src_type=op.src.type)
|
||||
|
||||
def visit_unbox(self, op: Unbox) -> None:
|
||||
self.emitter.emit_unbox(self.reg(op.src), self.reg(op), op.type)
|
||||
|
||||
def visit_unreachable(self, op: Unreachable) -> None:
|
||||
self.emitter.emit_line('CPy_Unreachable();')
|
||||
|
||||
def visit_raise_standard_error(self, op: RaiseStandardError) -> None:
|
||||
# TODO: Better escaping of backspaces and such
|
||||
if op.value is not None:
|
||||
if isinstance(op.value, str):
|
||||
message = op.value.replace('"', '\\"')
|
||||
self.emitter.emit_line(
|
||||
'PyErr_SetString(PyExc_{}, "{}");'.format(op.class_name, message))
|
||||
elif isinstance(op.value, Value):
|
||||
self.emitter.emit_line(
|
||||
'PyErr_SetObject(PyExc_{}, {});'.format(op.class_name,
|
||||
self.emitter.reg(op.value)))
|
||||
else:
|
||||
assert False, 'op value type must be either str or Value'
|
||||
else:
|
||||
self.emitter.emit_line('PyErr_SetNone(PyExc_{});'.format(op.class_name))
|
||||
self.emitter.emit_line('{} = 0;'.format(self.reg(op)))
|
||||
|
||||
def visit_call_c(self, op: CallC) -> None:
|
||||
if op.is_void:
|
||||
dest = ''
|
||||
else:
|
||||
dest = self.get_dest_assign(op)
|
||||
args = ', '.join(self.reg(arg) for arg in op.args)
|
||||
self.emitter.emit_line("{}{}({});".format(dest, op.function_name, args))
|
||||
|
||||
def visit_truncate(self, op: Truncate) -> None:
|
||||
dest = self.reg(op)
|
||||
value = self.reg(op.src)
|
||||
# for C backend the generated code are straight assignments
|
||||
self.emit_line("{} = {};".format(dest, value))
|
||||
|
||||
def visit_load_global(self, op: LoadGlobal) -> None:
|
||||
dest = self.reg(op)
|
||||
ann = ''
|
||||
if op.ann:
|
||||
s = repr(op.ann)
|
||||
if not any(x in s for x in ('/*', '*/', '\0')):
|
||||
ann = ' /* %s */' % s
|
||||
self.emit_line('%s = %s;%s' % (dest, op.identifier, ann))
|
||||
|
||||
def visit_int_op(self, op: IntOp) -> None:
|
||||
dest = self.reg(op)
|
||||
lhs = self.reg(op.lhs)
|
||||
rhs = self.reg(op.rhs)
|
||||
self.emit_line('%s = %s %s %s;' % (dest, lhs, op.op_str[op.op], rhs))
|
||||
|
||||
def visit_comparison_op(self, op: ComparisonOp) -> None:
|
||||
dest = self.reg(op)
|
||||
lhs = self.reg(op.lhs)
|
||||
rhs = self.reg(op.rhs)
|
||||
lhs_cast = ""
|
||||
rhs_cast = ""
|
||||
if op.op in (ComparisonOp.SLT, ComparisonOp.SGT, ComparisonOp.SLE, ComparisonOp.SGE):
|
||||
# Always signed comparison op
|
||||
lhs_cast = self.emit_signed_int_cast(op.lhs.type)
|
||||
rhs_cast = self.emit_signed_int_cast(op.rhs.type)
|
||||
elif op.op in (ComparisonOp.ULT, ComparisonOp.UGT, ComparisonOp.ULE, ComparisonOp.UGE):
|
||||
# Always unsigned comparison op
|
||||
lhs_cast = self.emit_unsigned_int_cast(op.lhs.type)
|
||||
rhs_cast = self.emit_unsigned_int_cast(op.rhs.type)
|
||||
elif isinstance(op.lhs, Integer) and op.lhs.value < 0:
|
||||
# Force signed ==/!= with negative operand
|
||||
rhs_cast = self.emit_signed_int_cast(op.rhs.type)
|
||||
elif isinstance(op.rhs, Integer) and op.rhs.value < 0:
|
||||
# Force signed ==/!= with negative operand
|
||||
lhs_cast = self.emit_signed_int_cast(op.lhs.type)
|
||||
self.emit_line('%s = %s%s %s %s%s;' % (dest, lhs_cast, lhs,
|
||||
op.op_str[op.op], rhs_cast, rhs))
|
||||
|
||||
def visit_load_mem(self, op: LoadMem) -> None:
|
||||
dest = self.reg(op)
|
||||
src = self.reg(op.src)
|
||||
# TODO: we shouldn't dereference to type that are pointer type so far
|
||||
type = self.ctype(op.type)
|
||||
self.emit_line('%s = *(%s *)%s;' % (dest, type, src))
|
||||
|
||||
def visit_set_mem(self, op: SetMem) -> None:
|
||||
dest = self.reg(op.dest)
|
||||
src = self.reg(op.src)
|
||||
dest_type = self.ctype(op.dest_type)
|
||||
# clang whines about self assignment (which we might generate
|
||||
# for some casts), so don't emit it.
|
||||
if dest != src:
|
||||
self.emit_line('*(%s *)%s = %s;' % (dest_type, dest, src))
|
||||
|
||||
def visit_get_element_ptr(self, op: GetElementPtr) -> None:
|
||||
dest = self.reg(op)
|
||||
src = self.reg(op.src)
|
||||
# TODO: support tuple type
|
||||
assert isinstance(op.src_type, RStruct)
|
||||
assert op.field in op.src_type.names, "Invalid field name."
|
||||
self.emit_line('%s = (%s)&((%s *)%s)->%s;' % (dest, op.type._ctype, op.src_type.name,
|
||||
src, op.field))
|
||||
|
||||
def visit_load_address(self, op: LoadAddress) -> None:
|
||||
typ = op.type
|
||||
dest = self.reg(op)
|
||||
src = self.reg(op.src) if isinstance(op.src, Register) else op.src
|
||||
self.emit_line('%s = (%s)&%s;' % (dest, typ._ctype, src))
|
||||
|
||||
def visit_keep_alive(self, op: KeepAlive) -> None:
|
||||
# This is a no-op.
|
||||
pass
|
||||
|
||||
# Helpers
|
||||
|
||||
def label(self, label: BasicBlock) -> str:
|
||||
return self.emitter.label(label)
|
||||
|
||||
def reg(self, reg: Value) -> str:
|
||||
if isinstance(reg, Integer):
|
||||
val = reg.value
|
||||
if val == 0 and is_pointer_rprimitive(reg.type):
|
||||
return "NULL"
|
||||
s = str(val)
|
||||
if val >= (1 << 31):
|
||||
# Avoid overflowing signed 32-bit int
|
||||
s += 'ULL'
|
||||
elif val == -(1 << 63):
|
||||
# Avoid overflowing C integer literal
|
||||
s = '(-9223372036854775807LL - 1)'
|
||||
elif val <= -(1 << 31):
|
||||
s += 'LL'
|
||||
return s
|
||||
else:
|
||||
return self.emitter.reg(reg)
|
||||
|
||||
def ctype(self, rtype: RType) -> str:
|
||||
return self.emitter.ctype(rtype)
|
||||
|
||||
def c_error_value(self, rtype: RType) -> str:
|
||||
return self.emitter.c_error_value(rtype)
|
||||
|
||||
def c_undefined_value(self, rtype: RType) -> str:
|
||||
return self.emitter.c_undefined_value(rtype)
|
||||
|
||||
def emit_line(self, line: str) -> None:
|
||||
self.emitter.emit_line(line)
|
||||
|
||||
def emit_lines(self, *lines: str) -> None:
|
||||
self.emitter.emit_lines(*lines)
|
||||
|
||||
def emit_inc_ref(self, dest: str, rtype: RType) -> None:
|
||||
self.emitter.emit_inc_ref(dest, rtype, rare=self.rare)
|
||||
|
||||
def emit_dec_ref(self, dest: str, rtype: RType, is_xdec: bool) -> None:
|
||||
self.emitter.emit_dec_ref(dest, rtype, is_xdec=is_xdec, rare=self.rare)
|
||||
|
||||
def emit_declaration(self, line: str) -> None:
|
||||
self.declarations.emit_line(line)
|
||||
|
||||
def emit_traceback(self, op: Branch) -> None:
|
||||
if op.traceback_entry is not None:
|
||||
globals_static = self.emitter.static_name('globals', self.module_name)
|
||||
self.emit_line('CPy_AddTraceback("%s", "%s", %d, %s);' % (
|
||||
self.source_path.replace("\\", "\\\\"),
|
||||
op.traceback_entry[0],
|
||||
op.traceback_entry[1],
|
||||
globals_static))
|
||||
if DEBUG_ERRORS:
|
||||
self.emit_line('assert(PyErr_Occurred() != NULL && "failure w/o err!");')
|
||||
|
||||
def emit_signed_int_cast(self, type: RType) -> str:
|
||||
if is_tagged(type):
|
||||
return '(Py_ssize_t)'
|
||||
else:
|
||||
return ''
|
||||
|
||||
def emit_unsigned_int_cast(self, type: RType) -> str:
|
||||
if is_int32_rprimitive(type):
|
||||
return '(uint32_t)'
|
||||
elif is_int64_rprimitive(type):
|
||||
return '(uint64_t)'
|
||||
else:
|
||||
return ''
|
||||
Loading…
Add table
Add a link
Reference in a new issue