implemented 'union' and 'unchecked' pragmas

This commit is contained in:
Araq
2014-03-05 20:19:04 +01:00
parent a066e4e5c5
commit 5506e8491d
9 changed files with 94 additions and 25 deletions

View File

@@ -488,6 +488,8 @@ const
routineKinds* = {skProc, skMethod, skIterator, skConverter,
skMacro, skTemplate}
tfIncompleteStruct* = tfVarargs
tfUncheckedArray* = tfVarargs
tfUnion* = tfNoSideEffect
skError* = skUnknown
# type flags that are essential for type equality:

View File

@@ -736,7 +736,7 @@ proc genArrayElem(p: BProc, e: PNode, d: var TLoc) =
var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs)
var first = intLiteral(firstOrd(ty))
# emit range check:
if (optBoundsCheck in p.options):
if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags:
if not isConstExpr(e.sons[1]):
# semantic pass has already checked for const index expressions
if firstOrd(ty) == 0:

View File

@@ -376,10 +376,13 @@ proc getTypePre(m: BModule, typ: PType): PRope =
else:
result = getSimpleTypeDesc(m, typ)
if result == nil: result = cacheGetType(m.typeCache, typ)
proc structOrUnion(t: PType): PRope =
(if tfUnion in t.flags: toRope("union") else: toRope("struct"))
proc getForwardStructFormat(): string =
if gCmd == cmdCompileToCpp: result = "struct $1;$n"
else: result = "typedef struct $1 $1;$n"
if gCmd == cmdCompileToCpp: result = "$1 $2;$n"
else: result = "typedef $1 $2 $2;$n"
proc getTypeForward(m: BModule, typ: PType): PRope =
result = cacheGetType(m.forwTypeCache, typ)
@@ -390,7 +393,8 @@ proc getTypeForward(m: BModule, typ: PType): PRope =
of tySequence, tyTuple, tyObject:
result = getTypeName(typ)
if not isImportedType(typ):
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
appf(m.s[cfsForwardTypes], getForwardStructFormat(),
[structOrUnion(typ), result])
idTablePut(m.forwTypeCache, typ, result)
else: internalError("getTypeForward(" & $typ.kind & ')')
@@ -445,7 +449,12 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
else: ae = sname
fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
appf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname])
let fieldType = field.loc.t
if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
appf(result, "$1 $2[SEQ_DECL_SIZE];$n",
[getTypeDescAux(m, fieldType.elemType, check), sname])
else:
appf(result, "$1 $2;$n", [getTypeDescAux(m, fieldType, check), sname])
else: internalError(n.info, "genRecordFieldsAux()")
proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope =
@@ -455,23 +464,24 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
check: var TIntSet): PRope =
# declare the record:
var hasField = false
let aStruct = structOrUnion(typ)
if typ.kind == tyObject:
if typ.sons[0] == nil:
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
result = ropecg(m, "struct $1 {$n", [name])
result = ropecg(m, "$1 $2 {$n", [aStruct, name])
else:
result = ropecg(m, "struct $1 {$n#TNimType* m_type;$n", [name])
result = ropecg(m, "$1 $2 {$n#TNimType* m_type;$n", [aStruct, name])
hasField = true
elif gCmd == cmdCompileToCpp:
result = ropecg(m, "struct $1 : public $2 {$n",
[name, getTypeDescAux(m, typ.sons[0], check)])
result = ropecg(m, "$1 $2 : public $3 {$n",
[aStruct, name, getTypeDescAux(m, typ.sons[0], check)])
hasField = true
else:
result = ropecg(m, "struct $1 {$n $2 Sup;$n",
[name, getTypeDescAux(m, typ.sons[0], check)])
result = ropecg(m, "$1 $2 {$n $3 Sup;$n",
[aStruct, name, getTypeDescAux(m, typ.sons[0], check)])
hasField = true
else:
result = ropef("struct $1 {$n", [name])
result = ropef("$1 $2 {$n", [aStruct, name])
var desc = getRecordFields(m, typ, check)
if (desc == nil) and not hasField:
appf(result, "char dummy;$n", [])
@@ -480,8 +490,8 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
app(result, "};" & tnl)
proc getTupleDesc(m: BModule, typ: PType, name: PRope,
check: var TIntSet): PRope =
result = ropef("struct $1 {$n", [name])
check: var TIntSet): PRope =
result = ropef("$1 $2 {$n", [structOrUnion(typ), name])
var desc: PRope = nil
for i in countup(0, sonsLen(typ) - 1):
appf(desc, "$1 Field$2;$n",
@@ -557,7 +567,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
if result == nil:
result = getTypeName(t)
if not isImportedType(t):
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
appf(m.s[cfsForwardTypes], getForwardStructFormat(),
[structOrUnion(t), result])
idTablePut(m.forwTypeCache, t, result)
assert(cacheGetType(m.typeCache, t) == nil)
idTablePut(m.typeCache, t, con(result, "*"))
@@ -588,7 +599,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
if result == nil:
result = getTypeName(t)
if not isImportedType(t):
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
appf(m.s[cfsForwardTypes], getForwardStructFormat(),
[structOrUnion(t), result])
idTablePut(m.forwTypeCache, t, result)
idTablePut(m.typeCache, t, result) # always call for sideeffects:
if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check)

View File

@@ -47,6 +47,7 @@ proc initDefines*() =
defineSymbol("nimeffects")
defineSymbol("nimbabel")
defineSymbol("nimcomputedgoto")
defineSymbol("nimunion")
# add platform specific symbols:
case targetCPU

View File

@@ -50,9 +50,9 @@ const
wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
wRaises, wTags}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
wInheritable, wGensym, wInject, wRequiresInit}
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
@@ -718,6 +718,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfIncompleteStruct)
of wUnchecked:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfUncheckedArray)
of wUnion:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfUnion)
of wRequiresInit:
noVal(it)
if sym.typ == nil: invalidPragma(it)

View File

@@ -63,7 +63,7 @@ type
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
wAsmNoStackFrame,
wImplicitStatic, wGlobal, wCodegenDecl,
wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked,
wAuto, wBool, wCatch, wChar, wClass,
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@@ -145,7 +145,7 @@ const
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
"computedgoto", "injectstmt",
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
"asmnostackframe", "implicitstatic", "global", "codegendecl",
"asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
"auto", "bool", "catch", "char", "class",
"const_cast", "default", "delete", "double",

View File

@@ -5320,6 +5320,53 @@ strings automatically:
printf("hallo %s", "world") # "world" will be passed as C string
Union pragma
------------
The `union`:idx: pragma can be applied to any ``object`` type. It means all
of the object's fields are overlaid in memory. This produces a ``union``
instead of a ``struct`` in the generated C/C++ code. The object declaration
then must not use inheritance or any GC'ed memory but this is currently not
checked.
**Future directions**: GC'ed memory should be allowed in unions and the GC
should scan unions conservatively.
Unchecked pragma
----------------
The `unchecked`:idx: pragma can be used to mark a named array as ``unchecked``
meaning its bounds are not checked. This is often useful when one wishes to
implement his own flexibly sized arrays. Additionally an unchecked array is
translated into a C array of undetermined size:
.. code-block:: nimrod
type
ArrayPart{.unchecked.} = array[0..0, int]
MySeq = object
len, cap: int
data: ArrayPart
Produces roughly this C code:
.. code-block:: C
typedef struct {
NI len;
NI cap;
NI data[];
} MySeq;
The bounds checking done at compile time is not disabled for now, so to access
``s.data[C]`` (where ``C`` is a constant) the array's index needs needs to
include ``C``.
The base type of the unchecked array may not contain any GC'ed memory but this
is currently not checked.
**Future directions**: GC'ed memory should be allowed in unchecked arrays and
there should be an explicit annotation of how the GC is to determine the
runtime size of the array.
Dynlib pragma for import
------------------------
With the `dynlib`:idx: pragma a procedure or a variable can be imported from

View File

@@ -285,8 +285,8 @@ static N_INLINE(NI32, float32ToInt32)(float x) {
typedef struct TStringDesc* string;
/* declared size of a sequence: */
#if defined(__GNUC__)
/* declared size of a sequence/variable length array: */
#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
# define SEQ_DECL_SIZE /* empty is correct! */
#else
# define SEQ_DECL_SIZE 1000000

View File

@@ -1,7 +1,6 @@
version 0.9.4
=============
- implement unchecked arrays
- make testament produce full JSON information
- fix gensym capture bug
- vm
@@ -28,7 +27,7 @@ version 0.9.x
=============
- memory manager: add a measure of fragmentation
- implement 'union' and 'bits' pragmas
- implement 'bits' pragmas
- fix closures/lambdalifting
- ensure (ref T)(a, b) works as a type conversion and type constructor
- optimize 'genericReset'; 'newException' leads to code bloat