summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorArne Döring <arne.doering@gmx.net>2019-11-13 09:22:41 +0100
committercooldome <cdome@bk.ru>2019-11-13 08:22:41 +0000
commit0496a666e22465eba9a309f0974220988f7f9920 (patch)
treed8393dceb5064a0f6bc1331d89759bb18a1a6a0c
parent84861eb48a1a7aa400b05a39b1d1dc1fc745aed2 (diff)
downloadNim-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.md3
-rw-r--r--compiler/ast.nim1
-rw-r--r--compiler/ccgtypes.nim2
-rw-r--r--compiler/cgen.nim8
-rw-r--r--compiler/pragmas.nim17
-rw-r--r--compiler/sizealignoffsetimpl.nim4
-rw-r--r--doc/manual.rst32
-rw-r--r--lib/nimbase.h1
-rw-r--r--scratch.nim1
-rw-r--r--tests/misc/globalalignas.nim3
-rw-r--r--tests/misc/talignas.nim46
-rw-r--r--tests/misc/tillegalalignas.nim7
-rw-r--r--tests/misc/tsizeof.nim47
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