diff options
author | Arne Döring <arne.doering@gmx.net> | 2019-11-13 09:22:41 +0100 |
---|---|---|
committer | cooldome <cdome@bk.ru> | 2019-11-13 08:22:41 +0000 |
commit | 0496a666e22465eba9a309f0974220988f7f9920 (patch) | |
tree | d8393dceb5064a0f6bc1331d89759bb18a1a6a0c | |
parent | 84861eb48a1a7aa400b05a39b1d1dc1fc745aed2 (diff) | |
download | Nim-0496a666e22465eba9a309f0974220988f7f9920.tar.gz |
implemented alignas pragma (#12643)
* implemented alignas pragma * fix bootstrap * generate c++ compatible syntax for alignas * Make it work. * Multiple alignof expressions. Implement top level alignof.
-rw-r--r-- | changelog.md | 3 | ||||
-rw-r--r-- | compiler/ast.nim | 1 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 2 | ||||
-rw-r--r-- | compiler/cgen.nim | 8 | ||||
-rw-r--r-- | compiler/pragmas.nim | 17 | ||||
-rw-r--r-- | compiler/sizealignoffsetimpl.nim | 4 | ||||
-rw-r--r-- | doc/manual.rst | 32 | ||||
-rw-r--r-- | lib/nimbase.h | 1 | ||||
-rw-r--r-- | scratch.nim | 1 | ||||
-rw-r--r-- | tests/misc/globalalignas.nim | 3 | ||||
-rw-r--r-- | tests/misc/talignas.nim | 46 | ||||
-rw-r--r-- | tests/misc/tillegalalignas.nim | 7 | ||||
-rw-r--r-- | tests/misc/tsizeof.nim | 47 |
13 files changed, 153 insertions, 19 deletions
diff --git a/changelog.md b/changelog.md index d1506f504..2618629d0 100644 --- a/changelog.md +++ b/changelog.md @@ -58,7 +58,8 @@ ## Language additions - +- `alignas` pragma can now be used similar to the `alignas` + declaration modifier in C/C++. ## Language changes diff --git a/compiler/ast.nim b/compiler/ast.nim index 06041eaa7..1ad7249d4 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -824,6 +824,7 @@ type of skLet, skVar, skField, skForVar: guard*: PSym bitsize*: int + alignment*: int # for alignas(X) expressions else: nil magic*: TMagic typ*: PType diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index a867c58c1..2e0f13bc5 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -549,6 +549,8 @@ proc genRecordFieldsAux(m: BModule, n: PNode, #assert(field.ast == nil) let sname = mangleRecFieldName(m, field) fillLoc(field.loc, locField, n, sname, OnUnknown) + if field.alignment > 0: + result.addf "alignas($1) ", [rope(field.alignment)] # for importcpp'ed objects, we only need to set field.loc, but don't # have to recurse via 'getTypeDescAux'. And not doing so prevents problems # with heavily templatized C++ code: diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 71d719814..1e25f44d1 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -478,7 +478,9 @@ proc localVarDecl(p: BProc; n: PNode): Rope = if s.loc.k == locNone: fillLoc(s.loc, locLocalVar, n, mangleLocalName(p, s), OnStack) if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) - result = getTypeDesc(p.module, s.typ) + if s.kind in {skLet, skVar} and s.alignment > 0: + result.addf("alignas($1) ", [rope(s.alignment)]) + result.add getTypeDesc(p.module, s.typ) if s.constraint.isNil: if sfRegister in s.flags: add(result, " register") #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: @@ -529,6 +531,8 @@ proc assignGlobalVar(p: BProc, n: PNode) = var decl: Rope = nil var td = getTypeDesc(p.module, s.loc.t) if s.constraint.isNil: + if s.alignment > 0: + decl.addf "alignas($1) ", [rope(s.alignment)] if p.hcrOn: add(decl, "static ") elif sfImportc in s.flags: add(decl, "extern ") add(decl, td) @@ -1187,6 +1191,8 @@ proc genVarPrototype(m: BModule, n: PNode) = declareThreadVar(m, sym, true) else: incl(m.declaredThings, sym.id) + if sym.alignment > 0: + m.s[cfsVars].addf "alignas($1) ", [rope(sym.alignment)] add(m.s[cfsVars], if m.hcrOn: "static " else: "extern ") add(m.s[cfsVars], getTypeDesc(m, sym.loc.t)) if m.hcrOn: add(m.s[cfsVars], "*") diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 205ed4309..24394bf97 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -20,7 +20,7 @@ const const declPragmas = {wImportc, wImportObjC, wImportCpp, wImportJs, wExportc, wExportCpp, - wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed} + wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed, wAlignas} ## common pragmas for declarations, to a good approximation procPragmas* = declPragmas + {FirstCallConv..LastCallConv, wMagic, wNoSideEffect, wSideEffect, wNoreturn, wDynlib, wHeader, @@ -803,13 +803,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, setExternName(c, sym, name, it.info) of wImportObjC: processImportObjC(c, sym, getOptionalStr(c, it, "$1"), it.info) - of wAlign: - if sym.typ == nil: invalidPragma(c, it) - var align = expectIntLit(c, it) - if (not isPowerOfTwo(align) and align != 0) or align >% high(int16): - localError(c.config, it.info, "power of two expected") - else: - sym.typ.align = align.int16 of wSize: if sym.typ == nil: invalidPragma(c, it) var size = expectIntLit(c, it) @@ -822,6 +815,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, sym.typ.align = floatInt64Align(c.config) else: localError(c.config, it.info, "size may only be 1, 2, 4 or 8") + of wAlignas: + let alignment = expectIntLit(c, it) + if alignment == 0: + discard + elif isPowerOfTwo(alignment): + sym.alignment = max(sym.alignment, alignment) + else: + localError(c.config, it.info, "power of two or 0 expected") of wNodecl: noVal(c, it) incl(sym.loc.flags, lfNoDecl) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index f57969712..e6bd1fc7b 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -156,6 +156,8 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, packed: bool, a size = n.sym.typ.size.int align = if packed: 1 else: n.sym.typ.align.int accum.align(align) + if n.sym.alignment > 0: + accum.align(n.sym.alignment) n.sym.offset = accum.offset accum.inc(size) else: @@ -183,6 +185,8 @@ proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; accum: var size = n.sym.typ.size.int align = n.sym.typ.align.int accum.align(align) + if n.sym.alignment > 0: + accum.align(n.sym.alignment) n.sym.offset = accum.offset accum.inc(size) else: diff --git a/doc/manual.rst b/doc/manual.rst index d1fa1ba07..71bfb1e2b 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6433,6 +6433,38 @@ generates: unsigned int flag:1; }; +Alignas pragma +-------------- + +The ``alignas`` pragma is for variables and object field members. It +modifies the alignment requirement of the thing being declared. The +argument must be a constant power of 2 or 0. Valid non-zero +alignments that are weaker than another alignas pragmas on the same +declaration are ignored. Alignments that are weaker that the +alignment requirement of the type are ignored. ``alignas(0)`` is +always ignored. + +.. code-block:: Nim + + type + sseType = object + sseData {.alignas(16).}: array[4,float32] + + # every object will be aligned to 128-byte boundary + Data = object + x: char + cacheline {.alignas(128).}: array[128, char] # over-aligned array of char, + + proc main() = + echo "sizeof(Data) = ", sizeof(Data), " (1 byte + 127 bytes padding + 128-byte array)" + # output: sizeof(Data) = 256 (1 byte + 127 bytes padding + 128-byte array) + echo "alignment of sseType is ", alignof(sseType) + # output: alignment of sseType is 16 + var d {.alignas(2048).}: Data # this instance of data is aligned even stricter + + main() + +This pragma has no effect on nimvm or the js backend. Volatile pragma --------------- diff --git a/lib/nimbase.h b/lib/nimbase.h index eadc34f66..fb6861ec7 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -256,6 +256,7 @@ __clang__ #include <limits.h> #include <stddef.h> +#include <stdalign.h> /* C99 compiler? */ #if (defined(__STD_VERSION__) && (__STD_VERSION__ >= 199901)) diff --git a/scratch.nim b/scratch.nim new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/scratch.nim @@ -0,0 +1 @@ + diff --git a/tests/misc/globalalignas.nim b/tests/misc/globalalignas.nim new file mode 100644 index 000000000..cc2a4cacb --- /dev/null +++ b/tests/misc/globalalignas.nim @@ -0,0 +1,3 @@ +var myglobal1* {.alignas(128).}: int32 +var myglobal2* {.alignas(128).}: int32 +var myglobal3* {.alignas(128).}: int32 diff --git a/tests/misc/talignas.nim b/tests/misc/talignas.nim new file mode 100644 index 000000000..fdf11c662 --- /dev/null +++ b/tests/misc/talignas.nim @@ -0,0 +1,46 @@ +discard """ +ccodeCheck: "\\i @'alignas(128) NI mylocal1' .*" +target: "c cpp" +output: "alignas ok" +""" + +# This is for Azure. The keyword ``alignof`` only exists in ``c++11`` +# and newer. On Azure gcc does not default to c++11 yet. +when defined(cpp) and not defined(windows): + {.passC: "-std=c++11".} + +import globalalignas + +var toplevel1 {.alignas: 32.} : int32 +var toplevel2 {.alignas: 32.} : int32 +var toplevel3 {.alignas: 32.} : int32 + +proc foobar() = + var myvar1 {.global, alignas(64).}: int = 123 + var myvar2 {.global, alignas(64).}: int = 123 + var myvar3 {.global, alignas(64).}: int = 123 + + doAssert (cast[uint](addr(myglobal1)) and 127) == 0 + doAssert (cast[uint](addr(myglobal2)) and 127) == 0 + doAssert (cast[uint](addr(myglobal3)) and 127) == 0 + + doAssert (cast[uint](addr(myvar1)) and 63) == 0 + doAssert (cast[uint](addr(myvar2)) and 63) == 0 + doAssert (cast[uint](addr(myvar3)) and 63) == 0 + + doAssert (cast[uint](addr(toplevel1)) and 31) == 0 + doAssert (cast[uint](addr(toplevel2)) and 31) == 0 + doAssert (cast[uint](addr(toplevel3)) and 31) == 0 + + # test multiple alignas expressions + var mylocal1 {.alignas(0), alignas(128), alignas(32).}: int = 123 + var mylocal2 {.alignas(128), alignas(0), alignas(32).}: int = 123 + var mylocal3 {.alignas(0), alignas(32), alignas(128).}: int = 123 + + doAssert (cast[uint](addr(mylocal1)) and 127) == 0 + doAssert (cast[uint](addr(mylocal2)) and 127) == 0 + doAssert (cast[uint](addr(mylocal3)) and 127) == 0 + + echo "alignas ok" + +foobar() diff --git a/tests/misc/tillegalalignas.nim b/tests/misc/tillegalalignas.nim new file mode 100644 index 000000000..7a9b031fb --- /dev/null +++ b/tests/misc/tillegalalignas.nim @@ -0,0 +1,7 @@ +discard """ +cmd: "nim check $options $file" +errormsg: "power of two or 0 expected" +""" + +proc foobar() = + let something {.alignas(33).} = 123 diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim index d5b8cada7..9bcdcb374 100644 --- a/tests/misc/tsizeof.nim +++ b/tests/misc/tsizeof.nim @@ -8,8 +8,8 @@ macros api OK ''' """ -# This is for travis. The keyword ``alignof`` only exists in ``c++11`` -# and newer. On travis gcc does not default to c++11 yet. +# This is for Azure. The keyword ``alignof`` only exists in ``c++11`` +# and newer. On Azure gcc does not default to c++11 yet. when defined(cpp) and not defined(windows): {.passC: "-std=c++11".} @@ -75,7 +75,7 @@ proc strAlign(arg: string): string = result &= ' ' macro c_offsetof(a: typed, b: untyped): int32 = - ## Buffet proof implementation that works on actual offsetof operator + ## Bullet proof implementation that works on actual offsetof operator ## in the c backend. Assuming of course this implementation is ## correct. let bliteral = @@ -89,7 +89,7 @@ macro c_offsetof(a: typed, b: untyped): int32 = res macro c_sizeof(a: typed): int32 = - ## Buffet proof implementation that works using the sizeof operator + ## Bullet proof implementation that works using the sizeof operator ## in the c backend. Assuming of course this implementation is ## correct. result = quote do: @@ -98,7 +98,7 @@ macro c_sizeof(a: typed): int32 = res macro c_alignof(arg: untyped): untyped = - ## Buffet proof implementation that works on actual alignment + ## Bullet proof implementation that works on actual alignment ## behavior measured at runtime. let typeSym = genSym(nskType, "AlignTestType"&arg.repr) result = quote do: @@ -313,7 +313,7 @@ testinstance: b: int8 c: int8 - PaddingOfSetEnum33 = object + PaddingOfSetEnum33 {.objectconfig.} = object cause: int8 theSet: set[MyEnum33] @@ -332,10 +332,17 @@ testinstance: c: char # from issue 4763 - GenericObject[T] = object + GenericObject[T] {.objectconfig.} = object a: int32 b: T + # this type mixes `packed` with `alignas`. + MyCustomAlignPackedObject {.objectconfig.} = object + a: char + b {.alignas: 32.}: int32 # alignas overrides `packed` for this field. + c: char + d: int32 # unaligned + const trivialSize = sizeof(TrivialType) # needs to be able to evaluate at compile time proc main(): void = @@ -350,6 +357,7 @@ testinstance: var ro : RootObj var go : GenericObject[int64] var po : PaddingOfSetEnum33 + var capo: MyCustomAlignPackedObject var e1: Enum1 @@ -368,8 +376,7 @@ testinstance: else: doAssert sizeof(SimpleAlignment) > 10 - testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go,po, e1, e2, e4, e8, eoa, eob) - + testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go,po, e1, e2, e4, e8, eoa, eob, capo) type WithBitsize {.objectconfig.} = object @@ -433,6 +440,11 @@ testinstance: testOffsetOf(RecursiveStuff, d1) testOffsetOf(RecursiveStuff, d2) + testOffsetOf(MyCustomAlignPackedObject, a) + testOffsetOf(MyCustomAlignPackedObject, b) + testOffsetOf(MyCustomAlignPackedObject, c) + testOffsetOf(MyCustomAlignPackedObject, d) + echo "body executed" # sanity check to ensure this logic isn't skipped entirely @@ -482,7 +494,24 @@ type a: int32 b: float32 + MyCustomAlignUnion {.union.} = object + c: char + a {.alignas: 32.}: int + + MyCustomAlignObject = object + c: char + a {.alignas: 32.}: int + doAssert sizeof(MyUnionType) == 4 +doAssert sizeof(MyCustomAlignUnion) == 32 +doAssert alignof(MyCustomAlignUnion) == 32 +doAssert sizeof(MyCustomAlignObject) == 64 +doAssert alignof(MyCustomAlignObject) == 32 + + + + + ########################################## # bug #9794 |