implemented 'union' and 'unchecked' pragmas

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

View file

@ -488,6 +488,8 @@ const
routineKinds* = {skProc, skMethod, skIterator, skConverter, routineKinds* = {skProc, skMethod, skIterator, skConverter,
skMacro, skTemplate} skMacro, skTemplate}
tfIncompleteStruct* = tfVarargs tfIncompleteStruct* = tfVarargs
tfUncheckedArray* = tfVarargs
tfUnion* = tfNoSideEffect
skError* = skUnknown skError* = skUnknown
# type flags that are essential for type equality: # 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 ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs)
var first = intLiteral(firstOrd(ty)) var first = intLiteral(firstOrd(ty))
# emit range check: # emit range check:
if (optBoundsCheck in p.options): if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags:
if not isConstExpr(e.sons[1]): if not isConstExpr(e.sons[1]):
# semantic pass has already checked for const index expressions # semantic pass has already checked for const index expressions
if firstOrd(ty) == 0: if firstOrd(ty) == 0:

View file

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

View file

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

View file

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

View file

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

View file

@ -5320,6 +5320,53 @@ strings automatically:
printf("hallo %s", "world") # "world" will be passed as C string 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 Dynlib pragma for import
------------------------ ------------------------
With the `dynlib`:idx: pragma a procedure or a variable can be imported from 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; typedef struct TStringDesc* string;
/* declared size of a sequence: */ /* declared size of a sequence/variable length array: */
#if defined(__GNUC__) #if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
# define SEQ_DECL_SIZE /* empty is correct! */ # define SEQ_DECL_SIZE /* empty is correct! */
#else #else
# define SEQ_DECL_SIZE 1000000 # define SEQ_DECL_SIZE 1000000

View file

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