Skip to content

Commit 4a7b4f4

Browse files
committed
Make enriched new/delete opt-in
1 parent c541647 commit 4a7b4f4

File tree

6 files changed

+161
-25
lines changed

6 files changed

+161
-25
lines changed

doc/1.4/language.md

+42-7
Original file line numberDiff line numberDiff line change
@@ -3478,13 +3478,25 @@ method n() -> (bool, int) {
34783478

34793479
<pre>
34803480
delete <em>expr</em>;
3481+
delete\<<em>spec</em>\> <em>expr</em>;
34813482
</pre>
34823483

3483-
Deallocates the memory pointed to by the result of evaluating
3484-
*`expr`*. The memory must have been allocated with the
3485-
`new` operator, and must not have been deallocated previously.
3486-
Equivalent to `delete` in C++; however, in DML, `delete`
3487-
can only be used as a statement, not as an expression.
3484+
Deallocates the memory pointed to by the result of evaluating *`expr`*.
3485+
3486+
*spec* specifies an *allocation format*, and can either be `enriched` or
3487+
`extern`. If not explicitly specified, *spec* will default to `extern`. This is
3488+
for backwards compatibility reasons &mdash; in the future the default will be
3489+
changed to be `enriched`.
3490+
3491+
The *enriched* format uses an allocation format specific to the device model,
3492+
and so can *only* be used in order to deallocate storage previously allocated
3493+
via [`new<enriched>`](#new-expressions) by the same device model.
3494+
3495+
The *extern* format compiles the `delete` statement to a use of `MM_FREE`,
3496+
meaning it may be used to deallocate storage previously allocated by any use of
3497+
Simics's memory allocation functions/macros (such as `MM_MALLOC`.) This includes
3498+
storage allocated via [`new<extern>`](#new-expressions) (which `new` without
3499+
allocation format specifier is equivalent to).
34883500

34893501
### Try Statements
34903502

@@ -4145,16 +4157,39 @@ independent method callback(int i, void *aux) {
41454157
new <em>type</em>
41464158

41474159
new <em>type</em>[<em>count</em>]
4160+
4161+
new\<<em>spec</em>\> <em>type</em>
4162+
4163+
new\<<em>spec</em>\> <em>type</em>[<em>count</em>]
41484164
</pre>
41494165

41504166
Allocates a chunk of memory large enough for a value of the specified
4151-
type. If the second form is used, memory for *count* values will
4167+
type. If a form specifying *count* is used, then memory for *count* values will
41524168
be allocated. The result is a pointer to the allocated memory. (The
41534169
pointer is never null; if allocation should fail, the Simics
41544170
application will be terminated.)
41554171

4172+
*spec* specifies an *allocation format*, and can either be `enriched` or
4173+
`extern`. If not explicitly specified, *spec* will default to `extern`. This is
4174+
for backwards compatibility reasons &mdash; in the future the default will be
4175+
changed to be `enriched`.
4176+
4177+
The *enriched* format uses an allocation format specific to the device model,
4178+
and *must* be used in order to allocate storage for values of [resource-enriched
4179+
(RAII) type](#raii-types). The fact the allocation format is model-specific
4180+
comes with the drawback that a pointer created with `new<enriched>` *cannot be
4181+
freed* using `MM_FREE`/`free`: only code from the same device model can free it,
4182+
and only by using [`delete<enriched>`](#delete-statements).
4183+
4184+
The *extern* format compiles `new` to a use of `MM_ZALLOC`, meaning a pointer
4185+
allocated this way may be freed using `MM_FREE` outside of the device model.
4186+
However, this format does not support allocating storage for values of
4187+
resource-enriched type.
4188+
41564189
When the memory is no longer needed, it should be deallocated using a
4157-
`delete` statement.
4190+
[`delete` statement](#delete-statements). The allocation format specified for the
4191+
`delete` statement *must* match that of the `new` expression used to allocate
4192+
the pointer.
41584193

41594194
### Cast Expressions
41604195

py/dml/codegen.py

+26-4
Original file line numberDiff line numberDiff line change
@@ -1600,13 +1600,22 @@ def expr_typeop(tree, location, scope):
16001600

16011601
@expression_dispatcher
16021602
def expr_new(tree, location, scope):
1603-
[t, count] = tree.args
1603+
[spec, t, count] = tree.args
16041604
(struct_defs, t) = eval_type(t, tree.site, location, scope)
1605+
if t.is_raii and spec != 'enriched':
1606+
report(ENEWRAII(tree.site, t.describe(),
1607+
'new' + f'<{spec}>'*(spec is not None)))
1608+
spec = 'enriched'
1609+
16051610
for (site, _) in struct_defs:
16061611
report(EANONSTRUCT(site, "'new' expression"))
16071612
if count:
16081613
count = codegen_expression(count, location, scope)
1609-
return mkNew(tree.site, t, count)
1614+
if spec == 'enriched':
1615+
return mkNew(tree.site, t, count)
1616+
else:
1617+
assert spec is None or spec == 'extern'
1618+
return mkNewExtern(tree.site, t, count)
16101619

16111620
@expression_dispatcher
16121621
def expr_apply(tree, location, scope):
@@ -3107,9 +3116,22 @@ def stmt_default(stmt, location, scope):
31073116

31083117
@statement_dispatcher
31093118
def stmt_delete(stmt, location, scope):
3110-
[expr] = stmt.args
3119+
[spec, expr] = stmt.args
31113120
expr = codegen_expression(expr, location, scope)
3112-
return [mkDelete(stmt.site, expr)]
3121+
etype = safe_realtype_shallow(expr.ctype())
3122+
if not isinstance(etype, TPtr):
3123+
raise ENOPTR(stmt.site, expr)
3124+
if etype.base.is_raii and spec != 'enriched':
3125+
report(EDELETERAII(stmt.site,
3126+
etype.base.describe(),
3127+
'delete' + f'<{spec}>'*(spec is not None)))
3128+
spec = 'enriched'
3129+
3130+
if spec == 'enriched':
3131+
return [mkDelete(stmt.site, expr)]
3132+
else:
3133+
assert spec is None or spec == 'extern'
3134+
return [mkDeleteExtern(stmt.site, expr)]
31133135

31143136
def probable_loggroups_specification(expr):
31153137
subexprs = [expr]

py/dml/ctree.py

+39-4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
'mkAssert',
5252
'mkReturn',
5353
'mkDelete',
54+
'mkDeleteExtern',
5455
'mkExpressionStatement',
5556
'mkAfter',
5657
'mkAfterOnHook',
@@ -126,6 +127,7 @@
126127
'mkHookSendRef', 'HookSendRef',
127128
'mkHookSendApply', 'HookSendApply',
128129
'mkNew',
130+
'mkNewExtern',
129131
#'Constant',
130132
'mkIntegerConstant', 'IntegerConstant',
131133
'mkIntegerLiteral',
@@ -582,8 +584,16 @@ def toc_stmt(self):
582584
self.linemark()
583585
out(f'DML_DELETE({self.expr.read()});\n')
584586

585-
def mkDelete(site, expr):
586-
return Delete(site, expr)
587+
mkDelete = Delete
588+
589+
class DeleteExtern(Statement):
590+
@auto_init
591+
def __init__(self, site, expr): pass
592+
def toc_stmt(self):
593+
self.linemark()
594+
out(f'MM_FREE({self.expr.read()});\n')
595+
596+
mkDeleteExtern = DeleteExtern
587597

588598
class ExpressionStatement(Statement):
589599
@auto_init
@@ -3329,9 +3339,9 @@ def __init__(self, site, newtype, count, raii_info):
33293339
self.type = TPtr(newtype)
33303340
def __str__(self):
33313341
if self.count:
3332-
return 'new %s[%s]' % (self.newtype, self.count)
3342+
return 'new<enriched> %s[%s]' % (self.newtype, self.count)
33333343
else:
3334-
return 'new %s' % self.newtype
3344+
return 'new<enriched> %s' % self.newtype
33353345
def read(self):
33363346
destructor = (self.raii_info.cident_destructor_array_item
33373347
if self.raii_info else '_dml_raii_destructor_ref_none')
@@ -3349,6 +3359,31 @@ def mkNew(site, newtype, count = None):
33493359
info = None
33503360
return New(site, newtype, count, info)
33513361

3362+
class NewExtern(Expression):
3363+
priority = 160 # f()
3364+
slots = ('type',)
3365+
@auto_init
3366+
def __init__(self, site, newtype, count):
3367+
self.type = TPtr(newtype)
3368+
def __str__(self):
3369+
if self.count:
3370+
return 'new<extern> %s[%s]' % (self.newtype, self.count)
3371+
else:
3372+
return 'new<extern> %s' % self.newtype
3373+
def read(self):
3374+
t = self.newtype.declaration('')
3375+
if self.count:
3376+
return 'MM_ZALLOC(%s, %s)' % (self.count.read(), t)
3377+
else:
3378+
return 'MM_ZALLOC(1, %s)' % (t)
3379+
3380+
3381+
def mkNewExtern(site, newtype, count = None):
3382+
assert not newtype.is_raii
3383+
if count:
3384+
count = as_int(count)
3385+
return NewExtern(site, newtype, count)
3386+
33523387
class ListItems(metaclass=abc.ABCMeta):
33533388
'''A series of consecutive list elements, where each list element
33543389
corresponds to one index in a multi-dimensional (but possibly

py/dml/dmlparse.py

+24-6
Original file line numberDiff line numberDiff line change
@@ -1824,15 +1824,33 @@ def typeop_arg_par(t):
18241824
'''typeoparg : LPAREN ctypedecl RPAREN'''
18251825
t[0] = t[2]
18261826

1827+
@prod_dml14
1828+
def maybe_newdelete_spec_yes(t):
1829+
'''maybe_newdelete_spec : LT ID GT
1830+
| LT EXTERN GT'''
1831+
supported_specs = ('extern', 'enriched')
1832+
spec = t[2]
1833+
if spec not in supported_specs:
1834+
suggestions = ' or '.join(f"'{spec}'" for spec in supported_specs)
1835+
report(ESYNTAX(site(t, 1), spec,
1836+
f"expected new/delete specification ({suggestions})"))
1837+
spec = 'enriched'
1838+
t[0] = spec
1839+
1840+
@prod
1841+
def maybe_newdelete_spec_no(t):
1842+
'''maybe_newdelete_spec : '''
1843+
t[0] = None
1844+
18271845
@prod
18281846
def expression_new(t):
1829-
'''expression : NEW ctypedecl'''
1830-
t[0] = ast.new(site(t), t[2], None)
1847+
'''expression : NEW maybe_newdelete_spec ctypedecl'''
1848+
t[0] = ast.new(site(t), t[2], t[3], None)
18311849

18321850
@prod
18331851
def expression_new_array(t):
1834-
'''expression : NEW ctypedecl LBRACKET expression RBRACKET'''
1835-
t[0] = ast.new(site(t), t[2], t[4])
1852+
'''expression : NEW maybe_newdelete_spec ctypedecl LBRACKET expression RBRACKET'''
1853+
t[0] = ast.new(site(t), t[2], t[3], t[5])
18361854

18371855
@prod
18381856
def expression_paren(t):
@@ -2200,8 +2218,8 @@ def case_blocks_list_hashifelse(t):
22002218
# Delete is an expression in C++, not a statement, but we don't care.
22012219
@prod
22022220
def statement_delete(t):
2203-
'statement_except_hashif : DELETE expression SEMI'
2204-
t[0] = ast.delete(site(t), t[2])
2221+
'statement_except_hashif : DELETE maybe_newdelete_spec expression SEMI'
2222+
t[0] = ast.delete(site(t), t[2], t[3])
22052223

22062224
@prod
22072225
def statent_try(t):

py/dml/messages.py

+28
Original file line numberDiff line numberDiff line change
@@ -1603,6 +1603,34 @@ class EANONRAIISTRUCT(DMLError):
16031603
fmt = ("method-local anonymous struct declared that has a member of "
16041604
+ "resource-enriched (RAII) type")
16051605

1606+
class ENEWRAII(DMLError):
1607+
"""
1608+
A `new` expression not specified as `enriched` can't be used to allocate a
1609+
storage for values of resource-enriched (RAII) type.
1610+
To address this, use `new<enriched>` instead of `new` or `new<extern>`.
1611+
"""
1612+
fmt = ("'new' expression not specified as 'enriched' used to create "
1613+
+ "pointer with resource-enriched (RAII) basetype '%s'. To address "
1614+
+ "this, use 'new<enriched>' instead of '%s', and ensure any "
1615+
+ "pointer allocated this way is only deallocated using "
1616+
+ "'delete<enriched>'!")
1617+
1618+
class EDELETERAII(DMLError):
1619+
"""
1620+
A `delete` statement not specified as `enriched` was used on a pointer with
1621+
resource-enriched (RAII) basetype. Except for extremely niche cases, this
1622+
is incorrect: an allocated pointer of resource-enriched basetype can only
1623+
be validly created through a `new` expression specified as `enriched`.
1624+
1625+
To address this, use `delete<enriched>` instead of `delete` or
1626+
`delete<extern>`.
1627+
"""
1628+
version = "1.4"
1629+
fmt = ("'delete' statement not specified as 'enriched' used on pointer "
1630+
+ "with resource-enriched (RAII) basetype '%s'. "
1631+
+ "To address this, use 'delete<enriched>' instead of '%s', and "
1632+
+ "ensure any pointer deallocated through this 'delete' is only "
1633+
+ "allocated using 'new<enriched>'!")
16061634

16071635
class EIDENTSIZEOF(DMLError):
16081636
"""

test/1.4/types/raii/T_various.dml

+2-4
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ method init() {
129129
assert prev_len == 36 && p->size == 128; // Shrunk to _DML_BITCEIL(36 + 1)*2
130130
s_ = "";
131131
assert p->size == 32; // Shrunk to _DML_STRING_INITIAL_SIZE
132-
local string *ps = new string;
132+
local string *ps = new<enriched> string;
133133
local int *pater = cast({1,2,3}, int[3]);
134134
assert pater[1] == 2;
135135
try {
@@ -153,12 +153,10 @@ method init() {
153153
assert *ps == "som bristande båge låga.";
154154
assert strcmp(cast(this, t).whatdoesthisevenmean,
155155
"En ondskans tid nu domnar min hand") == 0;
156-
delete ps;
156+
delete<enriched> ps;
157157

158158
local (string sa, string sb) = splitAt("FLYGANDESPJUT", 8);
159159
assert sa == "FLYGANDE" && sb == "SPJUT";
160-
local char *trab = new char[10];
161-
delete trab;
162160

163161
local (string sST, vect(int) vST) = memo();
164162
assert sST == "SOM EN VÄXANDE VÅG";

0 commit comments

Comments
 (0)