bugfix: wrong assertions for C++ code generation; some solaris support; first steps to an effect system

This commit is contained in:
Araq 2012-10-30 22:29:03 +01:00
commit 2133fbfcce
11 changed files with 193 additions and 22 deletions

View file

@ -277,6 +277,13 @@ const
# getting ready for the future expr/stmt merge # getting ready for the future expr/stmt merge
nkWhen* = nkWhenStmt nkWhen* = nkWhenStmt
nkWhenExpr* = nkWhenStmt nkWhenExpr* = nkWhenStmt
nkEffectList* = nkArgList
# hacks ahead: an nkEffectList is a node with 4 children:
exceptionEffects* = 0 # exceptions at position 0
readEffects* = 1 # read effects at position 1
writeEffects* = 2 # write effects at position 2
tagEffects* = 3 # user defined tag ('gc', 'time' etc.)
effectListLen* = 4 # list of effects list
type type
TTypeKind* = enum # order is important! TTypeKind* = enum # order is important!

View file

@ -1131,11 +1131,6 @@ proc writeModule(m: BModule, pending: bool) =
addFileToCompile(cfilenoext) addFileToCompile(cfilenoext)
addFileToLink(cfilenoext) addFileToLink(cfilenoext)
proc genPlatformAsserts(m: BModule) =
appf(m.s[cfsForwardTypes],
"typedef assert_numbits[sizeof(NI) == sizeof(void*) &&" &
"NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];$N")
proc myClose(b: PPassContext, n: PNode): PNode = proc myClose(b: PPassContext, n: PNode): PNode =
result = n result = n
if b == nil or passes.skipCodegen(n): return if b == nil or passes.skipCodegen(n): return
@ -1149,7 +1144,6 @@ proc myClose(b: PPassContext, n: PNode): PNode =
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
var disp = generateMethodDispatchers() var disp = generateMethodDispatchers()
for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym) for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
genPlatformAsserts(m)
genMainProc(m) genMainProc(m)
# we need to process the transitive closure because recursive module # we need to process the transitive closure because recursive module
# deps are allowed (and the system module is processed in the wrong # deps are allowed (and the system module is processed in the wrong

View file

@ -41,7 +41,7 @@ const
wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop, wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop,
wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated, wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated,
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
wLinearScanEnd, wPatterns} wLinearScanEnd, wPatterns, wEffects}
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame} wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame}
@ -292,14 +292,14 @@ proc processOption(c: PContext, n: PNode) =
if n.sons[1].kind != nkIdent: if n.sons[1].kind != nkIdent:
invalidPragma(n) invalidPragma(n)
else: else:
case whichKeyword(n.sons[1].ident) case n.sons[1].ident.s.normalize
of wSpeed: of "speed":
incl(gOptions, optOptimizeSpeed) incl(gOptions, optOptimizeSpeed)
excl(gOptions, optOptimizeSize) excl(gOptions, optOptimizeSize)
of wSize: of "size":
excl(gOptions, optOptimizeSpeed) excl(gOptions, optOptimizeSpeed)
incl(gOptions, optOptimizeSize) incl(gOptions, optOptimizeSize)
of wNone: of "none":
excl(gOptions, optOptimizeSpeed) excl(gOptions, optOptimizeSpeed)
excl(gOptions, optOptimizeSize) excl(gOptions, optOptimizeSize)
else: LocalError(n.info, errNoneSpeedOrSizeExpected) else: LocalError(n.info, errNoneSpeedOrSizeExpected)
@ -656,6 +656,9 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
of wEmit: PragmaEmit(c, it) of wEmit: PragmaEmit(c, it)
of wUnroll: PragmaUnroll(c, it) of wUnroll: PragmaUnroll(c, it)
of wLinearScanEnd: PragmaLinearScanEnd(c, it) of wLinearScanEnd: PragmaLinearScanEnd(c, it)
of wEffects:
# is later processed in effect analysis:
noVal(it)
of wIncompleteStruct: of wIncompleteStruct:
noVal(it) noVal(it)
if sym.typ == nil: invalidPragma(it) if sym.typ == nil: invalidPragma(it)

View file

@ -14,7 +14,7 @@ import
# way had some inherent problems. Performs: # way had some inherent problems. Performs:
# #
# * procvar checks # * procvar checks
# * effect tracking # * effect+exception tracking
# * closure analysis # * closure analysis
# * checks for invalid usages of compiletime magics (not implemented) # * checks for invalid usages of compiletime magics (not implemented)
# * checks for invalid usages of PNimNode (not implemented) # * checks for invalid usages of PNimNode (not implemented)
@ -29,6 +29,21 @@ import
# --> a TR macro can annotate the proc with user defined annotations # --> a TR macro can annotate the proc with user defined annotations
# --> the effect system can access these # --> the effect system can access these
# Load&Store analysis is performed on *paths*. A path is an access like
# obj.x.y[i].z; splitting paths up causes some problems:
#
# var x = obj.x
# var z = x.y[i].z
#
# Alias analysis is affected by this too! A good solution is *type splitting*:
# T becomes T1 and T2 if it's known that T1 and T2 can't alias.
#
# An aliasing problem and a race condition are effectively the same problem.
# Type based alias analysis is nice but not sufficient; especially splitting
# an array and filling it in parallel should be supported but is not easily
# done: It essentially requires a built-in 'indexSplit' operation and dependent
# typing.
proc sem2call(c: PContext, n: PNode): PNode = proc sem2call(c: PContext, n: PNode): PNode =
assert n.kind in nkCallKinds assert n.kind in nkCallKinds
@ -37,4 +52,145 @@ proc sem2call(c: PContext, n: PNode): PNode =
proc sem2sym(c: PContext, n: PNode): PNode = proc sem2sym(c: PContext, n: PNode): PNode =
assert n.kind == nkSym assert n.kind == nkSym
# ------------------------ exception tracking -------------------------------
discard """
exception tracking:
a() # raises 'x', 'e'
try:
b() # raises 'e'
except e:
# must not undo 'e' here; hrm
c()
--> we need a stack of scopes for this analysis
Effect tracking:
We track the effects per proc; forward declarations and indirect calls cause
problems: Forward declarations are computed lazily (we do this pass after
a whole module) and indirect calls are assumed the worst, unless they have
an effect annotation.
"""
type
TEffects = object
exc: PNode # stack of exceptions
bottom: int
PEffects = var TEffects
proc throws(tracked: PEffects, n: PNode) =
# since a 'raise' statement occurs rarely and we need distinct reasons;
# we simply do not merge anything here, this would be problematic for the
# stack of exceptions anyway:
tracked.exc.add n
proc excType(n: PNode): PType =
assert n.kind == nkRaiseStmt
# reraise is like raising E_Base:
let t = if n.sons[0].kind == nkEmpty: sysTypeFromName"E_Base"
else: n.sons[0].typ
result = skipTypes(t, skipPtrs)
proc mergeEffects(a: PEffects, b: PNode) =
var aa = a.exc
for effect in items(b):
block search
for i in a.bottom .. <aa.len:
if sameType(aa[i].excType, b.excType): break search
throws(a, effect)
proc listEffects(a: PEffects) =
var aa = a.exc
for e in items(aa):
Message(e.info, hintUser, renderTree(e))
proc catches(tracked: PEffects, e: PType) =
let e = skipTypes(e, skipPtrs)
let L = tracked.exc.len
var i = tracked.bottom
while i < L:
# e supertype of r?
if inheritanceDiff(e, tracked.exc[i].excType) <= 0:
tracked.exc.sons[i] = tracked.exc.sons[L-1]
dec L
else:
inc i
proc catchesAll(tracked: PEffects) =
setLen(tracked.exc.sons, tracked.bottom)
proc track(tracked: PEffects, n: PNode)
proc trackTryStmt(tracked: PEffects, n: PNode) =
let oldBottom = tracked.bottom
tracked.bottom = tracked.exc.len
track(tracked, n.sons[0])
for i in 1 .. < n.len:
let b = n.sons[i]
let blen = sonsLen(b)
if b.kind == nkExceptBranch:
if blen == 1:
catchesAll(tracked)
else:
for j in countup(0, blen - 2):
assert(b.sons[j].kind == nkType)
catches(tracked, b.sons[j].typ)
else:
assert b.kind == nkFinally
track(tracked, b.sons[blen-1])
tracked.bottom = oldBottom
proc isIndirectCall(n: PNode): bool =
result = n.kind != nkSym or n.sym.kind notin routineKinds
proc isForwardedProc(n: PNode): bool =
result = n.kind == nkSym and sfForward in n.sym.flags
proc trackPragmaStmt(tracked: PEffects, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
if whichPragma(it) == wEffects:
# list the computed effects up to here:
listEffects(tracked)
proc track(tracked: PEffects, n: PNode) =
case n.kind
of nkRaiseStmt: throws(tracked, n)
of nkCallNode:
# p's effects are ours too:
let op = n.sons[0].typ
InternalAssert op.kind == tyProc and op.n.sons[0].kind == nkEffectList
var effectList = op.n.sons[0]
if effectList.len == 0:
if isIndirectCall(n.sons[0]) or isForwardedProc(n.sons[0]):
# assume the worst: raise of exception 'E_Base':
var rs = newNodeI(nkRaiseStmt, n.info)
var re = newNodeIT(nkType, n.info, sysTypeFromName"E_Base")
rs.add(re)
effectList.add(rs)
mergeEffects(tracked, effectList)
of nkTryStmt:
trackTryStmt(tracked, n)
return
of nkPragma:
trackPragmaStmt(tracked, n)
return
else: nil
for i in 0 .. <safeLen(n):
track(tracked, n.sons[i])
proc trackProc*(s: PSym, body: PNode) =
var effects = s.typ.n.sons[0]
InternalAssert effects.kind == nkEffectList
# effects already computed?
if effects.len == effectListLen: return
newSeq(effects.sons, effectListLen)
var t: TEffects
t.exc = effects.sons[exceptionEffects]
track(t, body)

View file

@ -647,7 +647,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if genericParams != nil and sonsLen(genericParams) == 0: if genericParams != nil and sonsLen(genericParams) == 0:
cl = initIntSet() cl = initIntSet()
rawAddSon(result, nil) # return type rawAddSon(result, nil) # return type
res = newNodeI(nkType, n.info) # result.n[0] used to be `nkType`, but now it's `nkEffectList` because
# the effects are now stored in there too ... this is a bit hacky, but as
# usual we desperately try to save memory:
res = newNodeI(nkEffectList, n.info)
addSon(result.n, res) addSon(result.n, res)
var check = initIntSet() var check = initIntSet()
var counter = 0 var counter = 0

View file

@ -59,6 +59,8 @@ proc setupEnvironment =
#addFile(nimrodDir / r"tinyc\lib\libtcc1.c") #addFile(nimrodDir / r"tinyc\lib\libtcc1.c")
else: else:
addSysincludePath(gTinyC, "/usr/include") addSysincludePath(gTinyC, "/usr/include")
when defined(amd64):
addSysincludePath(gTinyC, "/usr/include/x86_64-linux-gnu")
proc compileCCode*(ccode: string) = proc compileCCode*(ccode: string) =
if not libIncluded: if not libIncluded:
@ -66,11 +68,11 @@ proc compileCCode*(ccode: string) =
setupEnvironment() setupEnvironment()
discard compileString(gTinyC, ccode) discard compileString(gTinyC, ccode)
proc run*() = proc run*() =
var a: array[0..1, cstring] var a: array[0..1, cstring]
a[0] = "" a[0] = ""
a[1] = "" a[1] = ""
var err = tinyc.run(gTinyC, 0'i32, addr(a)) != 0'i32 var err = tinyc.run(gTinyC, 0'i32, cast[cstringArray](addr(a))) != 0'i32
closeCCState(gTinyC) closeCCState(gTinyC)
if err: rawMessage(errExecutionOfProgramFailed, "") if err: rawMessage(errExecutionOfProgramFailed, "")

View file

@ -18,7 +18,7 @@ import
# Keywords must be kept sorted and within a range # Keywords must be kept sorted and within a range
type type
TSpecialWord* = enum TSpecialWord* = enum
wInvalid, wInvalid,
@ -52,7 +52,7 @@ type
wBoundchecks, wOverflowchecks, wNilchecks, wBoundchecks, wOverflowchecks, wNilchecks,
wFloatchecks, wNanChecks, wInfChecks, wFloatchecks, wNanChecks, wInfChecks,
wAssertions, wPatterns, wWarnings, wAssertions, wPatterns, wWarnings,
wHints, wOptimization, wSpeed, wSize, wNone, wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects,
wDeadCodeElim, wSafecode, wDeadCodeElim, wSafecode,
wPragma, wPragma,
wCompileTime, wNoInit, wCompileTime, wNoInit,
@ -62,7 +62,7 @@ type
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wAcyclic, wShallow, wUnroll, wLinearScanEnd,
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
wNoStackFrame, wNoStackFrame,
wImplicitStatic, wGlobal, wHoist wImplicitStatic, wGlobal, wHoist,
wAuto, wBool, wCatch, wChar, wClass, wAuto, wBool, wCatch, wChar, wClass,
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast, wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@ -133,7 +133,7 @@ const
"floatchecks", "nanchecks", "infchecks", "floatchecks", "nanchecks", "infchecks",
"assertions", "patterns", "warnings", "hints", "assertions", "patterns", "warnings", "hints",
"optimization", "speed", "size", "none", "optimization", "raises", "writes", "reads", "size", "effects",
"deadcodeelim", "safecode", "deadcodeelim", "safecode",
"pragma", "pragma",
"compiletime", "noinit", "compiletime", "noinit",

View file

@ -470,4 +470,6 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
# define GC_GUARD # define GC_GUARD
#endif #endif
typedef int assert_numbits[sizeof(NI) == sizeof(void*) &&
NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
#endif #endif

View file

@ -24,6 +24,9 @@ when defined(Windows):
import winlean import winlean
else: else:
import posix import posix
when defined(solaris):
{.passl: "-lsocket -lnsl".}
# Note: The enumerations are mapped to Window's constants. # Note: The enumerations are mapped to Window's constants.

View file

@ -4,5 +4,5 @@ type
var a: TArray var a: TArray
echo a[0] #OUT 0 echo a[0x0012] #OUT 0

View file

@ -37,7 +37,8 @@ version 0.9.XX
echo a echo a
echo b) echo b)
- implement the "snoopResult" pragma - implement the "snoopResult" pragma; no, make a strutils with string append
semantics instead ...
- implement "closure tuple consists of a single 'ref'" optimization - implement "closure tuple consists of a single 'ref'" optimization
- JS gen: - JS gen:
- fix exception handling - fix exception handling