Skip to content

Commit 16b5aa9

Browse files
committed
Port of RAII's writable/addressable/c_lval revamp
1 parent 6e9ff6b commit 16b5aa9

File tree

3 files changed

+76
-38
lines changed

3 files changed

+76
-38
lines changed

py/dml/codegen.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,7 @@ def expr_unop(tree, location, scope):
11631163
elif op == 'post--': return mkPostDec(tree.site, rh)
11641164
elif op == 'sizeof':
11651165
if (compat.dml12_misc not in dml.globals.enabled_compat
1166-
and not isinstance(rh, ctree.LValue)):
1166+
and not rh.addressable):
11671167
raise ERVAL(rh.site, 'sizeof')
11681168
return codegen_sizeof(tree.site, rh)
11691169
elif op == 'defined': return mkBoolConstant(tree.site, True)
@@ -1525,7 +1525,7 @@ def eval_type(asttype, site, location, scope, extern=False, typename=None,
15251525
etype = expr.node_type
15261526
else:
15271527
raise expr.exc()
1528-
elif (not isinstance(expr, ctree.LValue)
1528+
elif (not expr.addressable
15291529
and compat.dml12_misc not in dml.globals.enabled_compat):
15301530
raise ERVAL(expr.site, 'typeof')
15311531
else:

py/dml/ctree.py

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,21 +1187,12 @@ def truncate_int_bits(value, signed, bits=64):
11871187
return value & mask
11881188

11891189
class LValue(Expression):
1190-
"Somewhere to read or write data"
1190+
"""An expression whose C representation is always an LValue, whose address
1191+
is always safe to take, in the sense that the duration that address
1192+
remains valid is intuitively predictable by the user"""
11911193
writable = True
1192-
1193-
def write(self, source):
1194-
rt = realtype(self.ctype())
1195-
if isinstance(rt, TEndianInt):
1196-
return (f'{rt.dmllib_fun("copy")}(&{self.read()},'
1197-
+ f' {source.read()})')
1198-
return '%s = %s' % (self.read(), source.read())
1199-
1200-
@property
1201-
def is_stack_allocated(self):
1202-
'''Returns true only if it's known that writing to the lvalue will
1203-
write to stack-allocated data'''
1204-
return False
1194+
addressable = True
1195+
c_lval = True
12051196

12061197
class IfExpr(Expression):
12071198
priority = 30
@@ -2628,13 +2619,13 @@ def make_simple(cls, site, rh):
26282619
TPtr(TVoid())],
26292620
TVoid())))
26302621
if (compat.dml12_misc not in dml.globals.enabled_compat
2631-
and not isinstance(rh, LValue)):
2622+
and not rh.addressable):
26322623
raise ERVAL(rh.site, '&')
26332624
return AddressOf(site, rh)
26342625

26352626
@property
26362627
def is_pointer_to_stack_allocation(self):
2637-
return isinstance(self.rh, LValue) and self.rh.is_stack_allocated
2628+
return self.rh.is_stack_allocated
26382629

26392630
def mkAddressOf(site, rh):
26402631
if dml.globals.compat_dml12_int(site):
@@ -2672,7 +2663,8 @@ def is_stack_allocated(self):
26722663

26732664
@property
26742665
def is_pointer_to_stack_allocation(self):
2675-
return isinstance(self.type, TArray) and self.is_stack_allocated
2666+
return (isinstance(safe_realtype_shallow(self.type), TArray)
2667+
and self.is_stack_allocated)
26762668

26772669
mkDereference = Dereference.make
26782670

@@ -2794,7 +2786,7 @@ def mkUnaryPlus(site, rh):
27942786
rh, _ = promote_integer(rh, rhtype)
27952787
else:
27962788
raise ICE(site, "Unexpected arith argument to unary +")
2797-
if isinstance(rh, LValue):
2789+
if rh.addressable or rh.writable:
27982790
# +x is a rvalue
27992791
rh = mkRValue(rh)
28002792
return rh
@@ -2820,7 +2812,7 @@ def make_simple(cls, site, rh):
28202812
rhtype = safe_realtype(rh.ctype())
28212813
if not isinstance(rhtype, (IntegerType, TPtr)):
28222814
raise EINCTYPE(site, cls.op)
2823-
if not isinstance(rh, LValue):
2815+
if not rh.addressable:
28242816
if isinstance(rh, BitSlice):
28252817
hint = 'try %s= 1' % (cls.base_op[0],)
28262818
else:
@@ -4586,11 +4578,13 @@ def read(self):
45864578

45874579
mkStaticVariable = StaticVariable
45884580

4589-
class StructMember(LValue):
4581+
class StructMember(Expression):
45904582
priority = 160
45914583
explicit_type = True
45924584
@auto_init
45934585
def __init__(self, site, expr, sub, type, op):
4586+
# Write of StructMembers rely on them being C lvalues
4587+
assert not expr.writable or expr.c_lval
45944588
assert_type(site, expr, Expression)
45954589
assert_type(site, sub, str)
45964590

@@ -4607,11 +4601,12 @@ def read(self):
46074601

46084602
@property
46094603
def is_stack_allocated(self):
4610-
return isinstance(self.expr, LValue) and self.expr.is_stack_allocated
4604+
return self.expr.is_stack_allocated
46114605

46124606
@property
46134607
def is_pointer_to_stack_allocation(self):
4614-
return isinstance(self.type, TArray) and self.is_stack_allocated
4608+
return (isinstance(safe_realtype_shallow(self.type), TArray)
4609+
and self.is_stack_allocated)
46154610

46164611
def try_resolve_len(site, lh):
46174612
if isinstance(lh, NonValue):
@@ -4750,18 +4745,28 @@ def is_stack_allocated(self):
47504745

47514746
@property
47524747
def is_pointer_to_stack_allocation(self):
4753-
return isinstance(self.type, TArray) and self.is_stack_allocated
4748+
return (isinstance(safe_realtype_shallow(self.type), TArray)
4749+
and self.is_stack_allocated)
47544750

4755-
class VectorRef(LValue):
4751+
class VectorRef(Expression):
47564752
slots = ('type',)
47574753
@auto_init
47584754
def __init__(self, site, expr, idx):
4755+
assert not expr.writable or expr.c_lval
47594756
self.type = realtype(self.expr.ctype()).base
47604757
def read(self):
47614758
return 'VGET(%s, %s)' % (self.expr.read(), self.idx.read())
4762-
def write(self, source):
4763-
return "VSET(%s, %s, %s)" % (self.expr.read(), self.idx.read(),
4764-
source.read())
4759+
# No need for write, VGET results in an lvalue
4760+
4761+
@property
4762+
def writable(self):
4763+
return self.expr.writable
4764+
@property
4765+
def addressable(self):
4766+
return self.expr.addressable
4767+
@property
4768+
def c_lval(self):
4769+
return self.expr.c_lval
47654770

47664771
def mkIndex(site, expr, idx):
47674772
if isinstance(idx, NonValue):
@@ -4835,7 +4840,7 @@ def read(self):
48354840

48364841
@property
48374842
def is_pointer_to_stack_allocation(self):
4838-
return (isinstance(self.type, TPtr)
4843+
return (isinstance(safe_realtype_shallow(self.type), TPtr)
48394844
and self.expr.is_pointer_to_stack_allocation)
48404845

48414846
def mkCast(site, expr, new_type):
@@ -5008,12 +5013,17 @@ def explicit_type(self):
50085013
def type(self):
50095014
assert self.explicit_type
50105015
return self.expr.type
5016+
# Since addressable and readable are False this may only ever be leveraged
5017+
# by DMLC for optimization purposes
5018+
@property
5019+
def c_lval(self):
5020+
return self.expr.c_lval
50115021
@property
50125022
def is_pointer_to_stack_allocation(self):
50135023
return self.expr.is_pointer_to_stack_allocation
50145024

50155025
def mkRValue(expr):
5016-
if isinstance(expr, LValue) or expr.writable:
5026+
if expr.addressable or expr.writable:
50175027
return RValue(expr.site, expr)
50185028
return expr
50195029

py/dml/expr.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,19 @@ class Expression(Code):
109109
# bitslicing.
110110
explicit_type = False
111111

112-
# Can the expression be assigned to?
113-
# If writable is True, there is a method write() which returns a C
114-
# expression to make the assignment.
112+
# Can the expression be safely assigned to in DML?
113+
# This implies write() can be safely used.
115114
writable = False
116115

116+
# Can the address of the expression be taken safely in DML?
117+
# This implies c_lval, and typically implies writable.
118+
addressable = False
119+
120+
# Is the C representation of the expression an lvalue?
121+
# If True, then the default implementation of write() must not be
122+
# overridden.
123+
c_lval = False
124+
117125
def __init__(self, site):
118126
assert not site or isinstance(site, Site)
119127
self.site = site
@@ -139,10 +147,16 @@ def apply(self, inits, location, scope):
139147
'Apply this expression as a function'
140148
return mkApplyInits(self.site, self, inits, location, scope)
141149

150+
@property
151+
def is_stack_allocated(self):
152+
'''Returns true only if it's known that the storage for the value that
153+
this expression evaluates to is temporary to a method scope'''
154+
return False
155+
142156
@property
143157
def is_pointer_to_stack_allocation(self):
144158
'''Returns True only if it's known that the expression is a pointer
145-
to stack-allocated data'''
159+
to storage that is temporary to a method scope'''
146160
return False
147161

148162
def incref(self):
@@ -156,6 +170,17 @@ def copy(self, site):
156170
return type(self)(
157171
site, *(getattr(self, name) for name in self.init_args[2:]))
158172

173+
# Return a (principally) void-typed C expression that write a source to the
174+
# storage this expression represents
175+
# This should only be called if either writable or c_lval is True
176+
def write(self, source):
177+
assert self.c_lval
178+
rt = realtype(self.ctype())
179+
if isinstance(rt, TEndianInt):
180+
return (f'{rt.dmllib_fun("copy")}(&{self.read()},'
181+
+ f' {source.read()})')
182+
return '%s = %s' % (self.read(), source.read())
183+
159184
class NonValue(Expression):
160185
'''An expression that is not really a value, but which may validly
161186
appear as a subexpression of certain expressions.
@@ -202,11 +227,14 @@ def __str__(self):
202227
return self.str or self.cexpr
203228
def read(self):
204229
return self.cexpr
205-
def write(self, source):
206-
assert self.writable
207-
return "%s = %s" % (self.cexpr, source.read())
208230
@property
209231
def writable(self):
232+
return self.c_lval
233+
@property
234+
def addressable(self):
235+
return self.c_lval
236+
@property
237+
def c_lval(self):
210238
return self.type is not None
211239

212240
mkLit = Lit

0 commit comments

Comments
 (0)