Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement sizeof and alignof operator #5664

Closed
wants to merge 42 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
7ceb7ab
basic refactor
krux02 Apr 5, 2017
ccbd9e8
renamed computeSizeAux→computeSizeAndAlign
krux02 Apr 5, 2017
9ccfeb3
fix bug in times.nim
krux02 Apr 5, 2017
892bc99
format code
krux02 Apr 5, 2017
037c9c0
more refactoring
krux02 Apr 5, 2017
f3476a1
some work in progress
krux02 Apr 6, 2017
2f560f0
code reduction
krux02 Apr 8, 2017
e0334ab
WIP
krux02 Apr 11, 2017
cb14a8c
WIP
krux02 Apr 19, 2017
181a06d
add todo
krux02 May 4, 2017
376f22a
make bug visible
krux02 Apr 19, 2017
a0b17bc
a big step for me a small step for humanity
krux02 Apr 19, 2017
72d5d0d
my test now passes
krux02 Apr 19, 2017
aad3c89
removed debug information
krux02 Apr 19, 2017
db15fcc
removed old tsizeof.nim
krux02 Apr 19, 2017
a9eff03
move tsizeof into tests folder
krux02 Apr 19, 2017
7092c97
move size align offset into its own file
krux02 Apr 19, 2017
d6041b0
tsizeof now almost works for packed types
krux02 May 9, 2017
3dadd53
some renaming
krux02 May 19, 2017
802dc5d
fixes for breaking change of do notation
krux02 Jun 13, 2017
63a3a8f
WIP with debug output, and amend to trigger tests
krux02 Jul 14, 2017
e0e8e37
added bug
krux02 Sep 13, 2017
7a9419b
hard to find bug fixed, but still bugged
krux02 Sep 14, 2017
3e1800b
Merge branch 'devel' into sizeof-alignof
krux02 Aug 4, 2018
e7cf79d
post merge fix 1
krux02 Aug 4, 2018
8280f04
minimal fixes
krux02 Aug 5, 2018
9582a45
Merge branch 'devel' into sizeof-alignof
krux02 Aug 7, 2018
cbe99e1
fix for enum alignment
krux02 Aug 7, 2018
b302fd3
instance test size packed and unpacked
krux02 Aug 7, 2018
9b2aa8d
remove dead code, and make compilation fail at WIP
krux02 Aug 7, 2018
04c0a55
Merge branch 'devel' into sizeof-alignof
krux02 Oct 5, 2018
5f81489
update sizeof test, put back sizeof fallback
krux02 Oct 5, 2018
bf7db99
this should make tests on osx green
krux02 Oct 8, 2018
2cda6c4
Delete rodread.nim
krux02 Oct 8, 2018
1cd7a84
Delete rodwrite.nim
krux02 Oct 8, 2018
cda1ab4
better sizeof for imported types
krux02 Oct 8, 2018
209e8c0
remove debug output
krux02 Oct 8, 2018
2afcc69
let casting of imported types be a problem of the backend
krux02 Oct 9, 2018
c746166
detect illegal recursion in partially imported types
krux02 Oct 10, 2018
f7ad91b
sizeof unchecked array ;)
krux02 Oct 10, 2018
7a56147
Merge branch 'devel' into sizeof-alignof
krux02 Oct 10, 2018
b0b1b36
recursive tuples are detected again
krux02 Oct 11, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -326,13 +326,19 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
# TODO there is no proper way to find out if a type cannot be queried for the size.
let size = getSize(c.config, n[1].typ)
# We just assume here that the type might come from the c backend
if size > 0:
if size == szImportedType:
# Forward to the c code generation to emit a `sizeof` in the C code.
result = n
elif size >= 0:
Copy link
Member

@timotheecour timotheecour Oct 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will that work with incomplete types that have at least 1 field?

type
  MyStruct {.importc: "MyStruct".} = object
    field1: int
    # missing field2

see also #9250 for a more robust solution (that can be implemented after your PR)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

originally I was looking at the wrong location for the importc flag. Now it should work properly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@krux02 is my assessment in #9250 correct that currently you must pessimistically reject computing sizeof for any type annotated as importc ? feel free to comment in #9250

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are correct.

result = newIntNode(nkIntLit, size)
result.info = n.info
result.typ = n.typ
else:
# Forward to the c code generation to emit a `sizeof` in the C code.
result = n

localError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely")

result = nil


of mAlignOf:
result = newIntNode(nkIntLit, getAlign(c.config, n[1].typ))
Expand Down
38 changes: 23 additions & 15 deletions compiler/sizealignoffsetimpl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ proc align(address, alignment: BiggestInt): BiggestInt =
result = (address + (alignment - 1)) and not (alignment - 1)

const
szNonConcreteType* = -3
szImportedType* = -3
szIllegalRecursion* = -2
szUnknownSize* = -1
szUncomputedSize* = -1

proc computeSizeAlign(conf: ConfigRef; typ: PType): void

Expand Down Expand Up @@ -115,7 +115,7 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset:

else:
result.align = 1
result.offset = szNonConcreteType
result.offset = szImportedType


var recDepth = 0
Expand Down Expand Up @@ -162,20 +162,28 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf
result = n.sym.offset + n.sym.typ.size

else:
result = szNonConcreteType
result = szImportedType

# TODO this one needs an alignment map of the individual types

proc computeSizeAlign(conf: ConfigRef; typ: PType): void =
proc computeSizeAlign(conf: ConfigRef; typ: PType) =
## computes and sets ``size`` and ``align`` members of ``typ``

let hasSize = typ.size >= 0
let hasAlign = typ.align >= 0
let hasSize = typ.size >= 0 or typ.size == szImportedType
let hasAlign = typ.align >= 0 or typ.size == szImportedType

if hasSize and hasAlign:
# nothing to do, size and align already computed
return

if typ.sym != nil and typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc} and
typ.kind == tyObject:
echo "setting type as imported type"
debug(typ.sym)
typ.size = szImportedType
typ.align = szImportedType
return

# This function can only calculate both, size and align at the same time.
# If one of them is already set this value is stored here and reapplied
let revertSize = typ.size
Expand Down Expand Up @@ -260,8 +268,8 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType): void =

of tySet:
if typ.sons[0].kind == tyGenericParam:
typ.size = szUnknownSize
typ.align = szUnknownSize # in original version this was 1
typ.size = szUncomputedSize
typ.align = szUncomputedSize # in original version this was 1
else:
length = lengthOrd(conf, typ.sons[0])
if length <= 8:
Expand Down Expand Up @@ -370,8 +378,8 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType): void =
typ.size = typ.lastSon.size
typ.align = typ.lastSon.align
else:
typ.size = szUnknownSize
typ.align = szUnknownSize
typ.size = szUncomputedSize
typ.align = szUncomputedSize

of tyTypeDesc:
computeSizeAlign(conf, typ.base)
Expand All @@ -389,8 +397,8 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType): void =
typ.size = typ.lastSon.size
typ.align = typ.lastSon.align
else:
typ.size = szUnknownSize
typ.align = szUnknownSize
typ.size = szUncomputedSize
typ.align = szUncomputedSize
else:
typ.size = szUnknownSize
typ.align = szUnknownSize
typ.size = szUncomputedSize
typ.align = szUncomputedSize
5 changes: 0 additions & 5 deletions compiler/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1273,16 +1273,11 @@ proc getReturnType*(s: PSym): PType =

proc getAlign*(conf: ConfigRef; typ: PType): BiggestInt =
computeSizeAlign(conf, typ)

result = typ.align
if result < 0:
internalError(conf, "getAlign: " & $typ.kind)

proc getSize*(conf: ConfigRef; typ: PType): BiggestInt =
computeSizeAlign(conf, typ)
result = typ.size
if result < 0:
internalError(conf, "getSize: " & $typ.kind)

proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
case t.kind
Expand Down
2 changes: 2 additions & 0 deletions compiler/vmgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
# produces a value
else:
globalError(c.config, n.info, "expandToAst requires a call expression")
of mSizeOf, mAlignOf:
globalError(c.config, n.info, "cannot evaluate 'sizeof/alignof' because its type is not defined completely")
of mRunnableExamples:
discard "just ignore any call to runnableExamples"
else:
Expand Down
1 change: 0 additions & 1 deletion lib/posix/posix_other.nim
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,6 @@ type
sin_family*: TSa_Family ## AF_INET.
sin_port*: InPort ## Port number.
sin_addr*: InAddr ## IP address.
sin_zero: array[16 - 2 - 2 - 4, uint8]

In6Addr* {.importc: "struct in6_addr", pure, final,
header: "<netinet/in.h>".} = object ## struct in6_addr
Expand Down
9 changes: 4 additions & 5 deletions lib/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ proc unsafeNew*[T](a: var ref T, size: Natural) {.magic: "New", noSideEffect.}
## of the passed ``size``. This should only be used for optimization
## purposes when you know what you're doing!

proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect, compileTime.}
proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.}
## returns the size of ``x`` in bytes. Since this is a low-level proc,
## its usage is discouraged - using ``new`` for the most cases suffices
## that one never needs to know ``x``'s size. As a special semantic rule,
Expand All @@ -630,16 +630,15 @@ proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect, compileTime.}

when defined(nimHasalignOf):
proc alignof*[T](x: T): int {.magic: "AlignOf", noSideEffect.}
when defined(nimtypedescfixed):
proc alignof*(x: typedesc): int {.magic: "AlignOf", noSideEffect.}
proc alignof*(x: typedesc): int {.magic: "AlignOf", noSideEffect.}

proc offsetOfDotExpr(typeAccess: typed): int {.magic: "OffsetOf", noSideEffect, compileTime.}

template offsetOf*[T](t : typedesc[T]; member: untyped): int =
template offsetOf*[T](t: typedesc[T]; member: untyped): int =
var tmp: T
offsetOfDotExpr(tmp.member)

template offsetOf*[T](value : T; member: untyped): int =
template offsetOf*[T](value: T; member: untyped): int =
offsetOfDotExpr(value.member)

#proc offsetOf*(memberaccess: typed): int {.magic: "OffsetOf", noSideEffect.}
Expand Down
24 changes: 12 additions & 12 deletions lib/system/alloc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ type
next, prev: PBigChunk # chunks of the same (or bigger) size
data: AlignType # start of usable memory

template smallChunkOverhead: int = sizeof(SmallChunk)-sizeof(AlignType)
template bigChunkOverhead: int = sizeof(BigChunk)-sizeof(AlignType)
template smallChunkOverhead(): untyped = sizeof(SmallChunk)-sizeof(AlignType)
template bigChunkOverhead(): untyped = sizeof(BigChunk)-sizeof(AlignType)

# ------------- chunk table ---------------------------------------------------
# We use a PtrSet of chunk starts and a table[Page, chunksize] for chunk
Expand Down Expand Up @@ -371,7 +371,7 @@ iterator elements(t: IntSet): int {.inline.} =
r = r.next

proc isSmallChunk(c: PChunk): bool {.inline.} =
return c.size <= SmallChunkSize-smallChunkOverhead
return c.size <= SmallChunkSize-smallChunkOverhead()

proc chunkUnused(c: PChunk): bool {.inline.} =
result = (c.prevSize and 1) == 0
Expand Down Expand Up @@ -748,7 +748,7 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
var size = roundup(requestedSize, MemAlign)
sysAssert(size >= requestedSize, "insufficient allocated size!")
#c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
if size <= SmallChunkSize-smallChunkOverhead:
if size <= SmallChunkSize-smallChunkOverhead():
# allocate a small block: for small chunks, we use only its next pointer
var s = size div MemAlign
var c = a.freeSmallChunks[s]
Expand All @@ -758,7 +758,7 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
sysAssert c.size == PageSize, "rawAlloc 3"
c.size = size
c.acc = size
c.free = SmallChunkSize - smallChunkOverhead - size
c.free = SmallChunkSize - smallChunkOverhead() - size
c.next = nil
c.prev = nil
listAdd(a.freeSmallChunks[s], c)
Expand All @@ -771,7 +771,7 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
# c_fprintf(stdout, "csize: %lld; size %lld\n", c.size, size)
sysAssert c.size == size, "rawAlloc 6"
if c.freeList == nil:
sysAssert(c.acc + smallChunkOverhead + size <= SmallChunkSize,
sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize,
"rawAlloc 7")
result = cast[pointer](cast[ByteAddress](addr(c.data)) +% c.acc)
inc(c.acc, size)
Expand All @@ -787,7 +787,7 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
sysAssert(allocInv(a), "rawAlloc: before listRemove test")
listRemove(a.freeSmallChunks[s], c)
sysAssert(allocInv(a), "rawAlloc: end listRemove test")
sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead) %%
sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead()) %%
size == 0, "rawAlloc 21")
sysAssert(allocInv(a), "rawAlloc: end small size")
inc a.occ, size
Expand Down Expand Up @@ -846,11 +846,11 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
inc(c.free, s)
else:
inc(c.free, s)
if c.free == SmallChunkSize-smallChunkOverhead:
if c.free == SmallChunkSize-smallChunkOverhead():
listRemove(a.freeSmallChunks[s div MemAlign], c)
c.size = SmallChunkSize
freeBigChunk(a, cast[PBigChunk](c))
sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead) %%
sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %%
s == 0, "rawDealloc 2")
else:
# set to 0xff to check for usage after free bugs:
Expand All @@ -874,7 +874,7 @@ proc isAllocatedPtr(a: MemRegion, p: pointer): bool =
if isSmallChunk(c):
var c = cast[PSmallChunk](c)
var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
smallChunkOverhead
smallChunkOverhead()
result = (c.acc >% offset) and (offset %% c.size == 0) and
(cast[ptr FreeCell](p).zeroField >% 1)
else:
Expand All @@ -892,7 +892,7 @@ proc interiorAllocatedPtr(a: MemRegion, p: pointer): pointer =
if isSmallChunk(c):
var c = cast[PSmallChunk](c)
var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
smallChunkOverhead
smallChunkOverhead()
if c.acc >% offset:
sysAssert(cast[ByteAddress](addr(c.data)) +% offset ==
cast[ByteAddress](p), "offset is not what you think it is")
Expand Down Expand Up @@ -927,7 +927,7 @@ proc ptrSize(p: pointer): int =
sysAssert(not chunkUnused(c), "ptrSize")
result = c.size -% sizeof(FreeCell)
if not isSmallChunk(c):
dec result, bigChunkOverhead
dec result, bigChunkOverhead()

proc alloc(allocator: var MemRegion, size: Natural): pointer {.gcsafe.} =
result = rawAlloc(allocator, size+sizeof(FreeCell))
Expand Down
2 changes: 0 additions & 2 deletions tests/misc/tsizeof.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ discard """
output: "OK"
"""

# TODO test with small sized kind

type
TMyEnum = enum
tmOne, tmTwo, tmThree, tmFour
Expand Down
11 changes: 11 additions & 0 deletions tests/misc/tsizeof2.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
discard """
errormsg: "cannot evaluate 'sizeof/alignof' because its type is not defined completely"
line: 9
"""

type
MyStruct {.importc: "MyStruct".} = object

const i = sizeof(MyStruct)

echo i