Skip to content

Commit

Permalink
Type Equality/Compatibility Revamp
Browse files Browse the repository at this point in the history
Taken from #213, but with some refinements
  • Loading branch information
lwaern-intel committed May 27, 2024
1 parent ecb616f commit 882baa9
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 123 deletions.
18 changes: 3 additions & 15 deletions py/dml/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1306,18 +1306,7 @@ def expr_cast(tree, location, scope):
for (site, _) in struct_defs:
report(EANONSTRUCT(site, "'cast' expression"))

if (compat.dml12_misc in dml.globals.enabled_compat
and isinstance(expr, InterfaceMethodRef)):
# Workaround for SIMICS-9868
return mkLit(tree.site, "%s->%s" % (
expr.node_expr.read(), expr.method_name), type)

if isinstance(expr, NonValue) and (
not isinstance(expr, NodeRef)
or not isinstance(safe_realtype(type), TTrait)):
raise expr.exc()
else:
return mkCast(tree.site, expr, type)
return mkCast(tree.site, expr, type)

@expression_dispatcher
def expr_undefined(tree, location, scope):
Expand Down Expand Up @@ -2008,9 +1997,8 @@ def mk_sym(name, typ, mkunique=not dml.globals.debuggable):
for (name, typ) in decls:
sym = mk_sym(name, typ)
tgt_typ = safe_realtype_shallow(typ)
if tgt_typ.const:
nonconst_typ = tgt_typ.clone()
nonconst_typ.const = False
if shallow_const(tgt_typ):
nonconst_typ = safe_realtype_unconst(tgt_typ)
tgt_sym = mk_sym('_tmp_' + name, nonconst_typ, True)
sym.init = ExpressionInitializer(mkLocalVariable(stmt.site,
tgt_sym))
Expand Down
66 changes: 44 additions & 22 deletions py/dml/ctree.py
Original file line number Diff line number Diff line change
Expand Up @@ -1222,7 +1222,7 @@ def mkIfExpr(site, cond, texpr, fexpr):
(texpr, fexpr, utype) = usual_int_conv(
texpr, ttype, fexpr, ftype)
else:
if not compatible_types(ttype, ftype):
if not compatible_types_fuzzy(ttype, ftype):
raise EBINOP(site, ':', texpr, fexpr)
# TODO: in C, the rules are more complex,
# but our type system is too primitive to cover that
Expand Down Expand Up @@ -1396,7 +1396,7 @@ def make(cls, site, lh, rh):
if ((lhtype.is_arith and rhtype.is_arith)
or (isinstance(lhtype, (TPtr, TArray))
and isinstance(rhtype, (TPtr, TArray))
and compatible_types(lhtype.base, rhtype.base))):
and compatible_types_fuzzy(lhtype.base, rhtype.base))):
return cls.make_simple(site, lh, rh)
raise EILLCOMP(site, lh, lhtype, rh, rhtype)

Expand Down Expand Up @@ -1601,7 +1601,7 @@ def make(cls, site, lh, rh):
if ((lhtype.is_arith and rhtype.is_arith)
or (isinstance(lhtype, (TPtr, TArray))
and isinstance(rhtype, (TPtr, TArray))
and compatible_types(lhtype, rhtype))
and compatible_types_fuzzy(lhtype, rhtype))
or (isinstance(lhtype, TBool) and isinstance(rhtype, TBool))):
return Equals(site, lh, rh)

Expand Down Expand Up @@ -2932,8 +2932,8 @@ def mkInterfaceMethodRef(site, iface_node, indices, method_name):
if (not isinstance(ftype, TPtr)
or not isinstance(ftype.base, TFunction)
or not ftype.base.input_types
or TPtr(safe_realtype(TNamed('conf_object_t'))).cmp(
safe_realtype(ftype.base.input_types[0])) != 0):
or TPtr(safe_realtype_unconst(TNamed('conf_object_t'))).cmp(
safe_realtype_unconst(ftype.base.input_types[0])) != 0):
# non-method members are not accessible
raise EMEMBER(site, struct_name, method_name)

Expand Down Expand Up @@ -4674,7 +4674,10 @@ class ArrayRef(LValue):
explicit_type = True
@auto_init
def __init__(self, site, expr, idx):
self.type = realtype_shallow(expr.ctype()).base
expr_type = realtype_shallow(expr.ctype())
self.type = conv_const(expr_type.const
and isinstance(expr_type, TArray),
expr_type.base)
def __str__(self):
return '%s[%s]' % (self.expr, self.idx)
def read(self):
Expand Down Expand Up @@ -4787,30 +4790,41 @@ def mkCast(site, expr, new_type):
raise ETEMPLATEUPCAST(site, "object", new_type)
else:
return mkTraitUpcast(site, expr, real.trait)

if (compat.dml12_misc in dml.globals.enabled_compat
and isinstance(expr, InterfaceMethodRef)):
# Workaround for SIMICS-9868
return mkLit(site, "%s->%s" % (
expr.node_expr.read(), expr.method_name), new_type)

if isinstance(expr, NonValue):
raise expr.exc()
old_type = safe_realtype(expr.ctype())
if (dml.globals.compat_dml12_int(site)
and (isinstance(old_type, (TStruct, TVector))
or isinstance(real, (TStruct, TVector)))):
# these casts are permitted by C only if old and new are
# the same type, which is useless
return Cast(site, expr, new_type)
if isinstance(real, TStruct):
if isinstance(old_type, TStruct) and old_type.label == real.label:
return expr
raise ECAST(site, expr, new_type)
if isinstance(real, TExternStruct):
if isinstance(old_type, TExternStruct) and old_type.id == real.id:
return expr
if isinstance(real, (TVoid, TArray, TFunction)):
raise ECAST(site, expr, new_type)
if isinstance(real, (TVoid, TArray, TVector, TTraitList, TFunction)):
if old_type.cmp(real) == 0:
if (old_type.is_int
and not old_type.is_endian
and dml.globals.compat_dml12_int(expr.site)):
# 1.2 integer expressions often lie about their actual type,
# and require a "redundant" cast! Why yes, this IS horrid!
return Cast(site, expr, new_type)
return mkRValue(expr)
if isinstance(real, (TStruct, TExternStruct, TVector, TTraitList)):
raise ECAST(site, expr, new_type)
if isinstance(old_type, (TVoid, TStruct, TVector, TTraitList, TTrait)):
raise ECAST(site, expr, new_type)
if old_type.is_int and old_type.is_endian:
expr = as_int(expr)
old_type = safe_realtype(expr.ctype())
if real.is_int and not real.is_endian:
if isinstance(expr, IntegerConstant):
if old_type.is_int and expr.constant:
value = truncate_int_bits(expr.value, real.signed, real.bits)
if dml.globals.compat_dml12_int(site):
return IntegerConstant_dml12(site, value, real)
Expand All @@ -4821,8 +4835,8 @@ def mkCast(site, expr, new_type):
# Shorten redundant chains of integer casts. Avoids insane C
# output for expressions like a+b+c+d.
if (isinstance(expr, Cast)
and isinstance(expr.type, TInt)
and expr.type.bits >= real.bits):
and isinstance(old_type, TInt)
and old_type.bits >= real.bits):
# (uint64)(int64)x -> (uint64)x
expr = expr.expr
old_type = safe_realtype(expr.ctype())
Expand Down Expand Up @@ -4858,9 +4872,7 @@ def mkCast(site, expr, new_type):
return expr
elif real.is_int and real.is_endian:
old_type = safe_realtype(expr.ctype())
if real.cmp(old_type) == 0:
return expr
elif old_type.is_arith or isinstance(old_type, TPtr):
if old_type.is_arith or isinstance(old_type, TPtr):
return mkApply(
expr.site,
mkLit(expr.site, *real.get_store_fun()),
Expand Down Expand Up @@ -4917,7 +4929,6 @@ def mkCast(site, expr, new_type):
class RValue(Expression):
'''Wraps an lvalue to prohibit write. Useful when a composite
expression is reduced down to a single variable.'''
writable = False
@auto_init
def __init__(self, site, expr): pass
def __str__(self):
Expand All @@ -4926,11 +4937,22 @@ def ctype(self):
return self.expr.ctype()
def read(self):
return self.expr.read()
def discard(self): pass
def discard(self):
return self.expr.discard()
def incref(self):
self.expr.incref()
def decref(self):
self.expr.decref()
@property
def explicit_type(self):
return self.expr.explicit_type
@property
def type(self):
assert self.explicit_type
return self.expr.type
@property
def is_pointer_to_stack_allocation(self):
return self.expr.is_pointer_to_stack_allocation

def mkRValue(expr):
if isinstance(expr, LValue) or expr.writable:
Expand Down
2 changes: 2 additions & 0 deletions py/dml/ctree_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1416,6 +1416,8 @@ def const_types(self):
# abstract type
types.IntegerType,
# abstract type
types.ArchDependentIntegerType,
# abstract type
types.StructType,
# 1.2, weird
types.TUnknown,
Expand Down
6 changes: 4 additions & 2 deletions py/dml/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,8 @@ def typecheck_method_override(m1, m2, location):
# TODO move to caller
(_, type1) = eval_type(t1, a1.site, location, global_scope)
(_, type2) = eval_type(t2, a2.site, location, global_scope)
if safe_realtype(type1).cmp(safe_realtype(type2)) != 0:
if safe_realtype_unconst(type1).cmp(
safe_realtype_unconst(type2)) != 0:
raise EMETH(a1.site, a2.site,
f"mismatching types in input argument {n1}")

Expand All @@ -683,7 +684,8 @@ def typecheck_method_override(m1, m2, location):
((n1, t1), (n2, t2)) = (a1.args, a2.args)
(_, type1) = eval_type(t1, a1.site, location, global_scope)
(_, type2) = eval_type(t2, a2.site, location, global_scope)
if safe_realtype(type1).cmp(safe_realtype(type2)) != 0:
if safe_realtype_unconst(type1).cmp(
safe_realtype_unconst(type2)) != 0:
msg = "mismatching types in return value"
if len(outp1) > 1:
msg += f" {i + 1}"
Expand Down
4 changes: 2 additions & 2 deletions py/dml/traits.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,11 +398,11 @@ def typecheck_method_override(left, right):
if throws0 != throws1:
raise EMETH(site0, site1, "different nothrow annotations")
for ((n, t0), (_, t1)) in zip(inp0, inp1):
if realtype(t0).cmp(realtype(t1)) != 0:
if safe_realtype_unconst(t0).cmp(safe_realtype_unconst(t1)) != 0:
raise EMETH(site0, site1,
"mismatching types in input argument %s" % (n,))
for (i, ((_, t0), (_, t1))) in enumerate(zip(outp0, outp1)):
if realtype(t0).cmp(realtype(t1)) != 0:
if safe_realtype_unconst(t0).cmp(safe_realtype_unconst(t1)) != 0:
raise EMETH(site0, site1,
"mismatching types in output argument %d" % (i + 1,))

Expand Down
Loading

0 comments on commit 882baa9

Please sign in to comment.