# # # The Nimrod Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This module declares some helpers for the C code generator. import ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg, platform, trees proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode = case n.kind of nkStmtList: for i in 0 .. < n.len: result = getPragmaStmt(n[i], w) if result != nil: break of nkPragma: for i in 0 .. < n.len: if whichPragma(n[i]) == w: return n[i] else: nil proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool = result = getPragmaStmt(n, w) != nil proc hashString*(s: string): biggestInt = # has to be the same algorithm as system.hashString! if CPU[targetCPU].bit == 64: # we have to use the same bitwidth # as the target CPU var b = 0'i64 for i in countup(0, len(s) - 1): b = b +% Ord(s[i]) b = b +% `shl`(b, 10) b = b xor `shr`(b, 6) b = b +% `shl`(b, 3) b = b xor `shr`(b, 11) b = b +% `shl`(b, 15) result = b else: var a = 0'i32 for i in countup(0, len(s) - 1): a = a +% Ord(s[i]).int32 a = a +% `shl`(a, 10'i32) a = a xor `shr`(a, 6'i32) a = a +% `shl`(a, 3'i32) a = a xor `shr`(a, 11'i32) a = a +% `shl`(a, 15'i32) result = a var gTypeTable: array[TTypeKind, TIdTable] gCanonicalTypes: array[TTypeKind, PType] proc initTypeTables() = for i in countup(low(TTypeKind), high(TTypeKind)): InitIdTable(gTypeTable[i]) when false: proc echoStats*() = for i in countup(low(TTypeKind), high(TTypeKind)): echo i, " ", gTypeTable[i].counter proc GetUniqueType*(key: PType): PType = # this is a hotspot in the compiler! if key == nil: return var k = key.kind case k of tyBool, tyChar, tyInt..tyUInt64: # no canonicalization for integral types, so that e.g. ``pid_t`` is # produced instead of ``NI``. result = key of tyEmpty, tyNil, tyExpr, tyStmt, tyTypeDesc, tyPointer, tyString, tyCString, tyNone, tyBigNum: result = gCanonicalTypes[k] if result == nil: gCanonicalTypes[k] = key result = key of tyGenericParam, tyTypeClass: InternalError("GetUniqueType") of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter: result = GetUniqueType(lastSon(key)) of tyArrayConstr, tyGenericInvokation, tyGenericBody, tyOpenArray, tyArray, tySet, tyRange, tyTuple, tyPtr, tyRef, tySequence, tyForward, tyVarargs, tyProxy, tyVar: # tuples are quite horrible as C does not support them directly and # tuple[string, string] is a (strange) subtype of # tuple[nameA, nameB: string]. This bites us here, so we # use 'sameBackendType' instead of 'sameType'. # we have to do a slow linear search because types may need # to be compared by their structure: if IdTableHasObjectAsKey(gTypeTable[k], key): return key for h in countup(0, high(gTypeTable[k].data)): var t = PType(gTypeTable[k].data[h].key) if t != nil and sameBackendType(t, key): return t IdTablePut(gTypeTable[k], key, key) result = key of tyObject: if tfFromGeneric notin key.flags: # fast case; lookup per id suffices: result = PType(IdTableGet(gTypeTable[k], key)) if result == nil: IdTablePut(gTypeTable[k], key, key) result = key else: # ugly slow case: need to compare by structure if IdTableHasObjectAsKey(gTypeTable[k], key): return key for h in countup(0, high(gTypeTable[k].data)): var t = PType(gTypeTable[k].data[h].key) if t != nil and sameType(t, key): return t IdTablePut(gTypeTable[k], key, key) result = key of tyEnum: result = PType(IdTableGet(gTypeTable[k], key)) if result == nil: IdTablePut(gTypeTable[k], key, key) result = key of tyProc: # tyVar is not 100% correct, but would speeds things up a little: if key.callConv != ccClosure: result = key else: # ugh, we need the canon here: if IdTableHasObjectAsKey(gTypeTable[k], key): return key for h in countup(0, high(gTypeTable[k].data)): var t = PType(gTypeTable[k].data[h].key) if t != nil and sameBackendType(t, key): return t IdTablePut(gTypeTable[k], key, key) result = key proc TableGetType*(tab: TIdTable, key: PType): PObject = # returns nil if we need to declare this type result = IdTableGet(tab, key) if (result == nil) and (tab.counter > 0): # we have to do a slow linear search because types may need # to be compared by their structure: for h in countup(0, high(tab.data)): var t = PType(tab.data[h].key) if t != nil: if sameType(t, key): return tab.data[h].val proc toCChar*(c: Char): string = case c of '\0'..'\x1F', '\x80'..'\xFF': result = '\\' & toOctal(c) of '\'', '\"', '\\': result = '\\' & c else: result = $(c) proc makeSingleLineCString*(s: string): string = result = "\"" for c in items(s): result.add(c.toCChar) result.add('\"') proc makeCString*(s: string): PRope = # BUGFIX: We have to split long strings into many ropes. Otherwise # this could trigger an InternalError(). See the ropes module for # further information. const MaxLineLength = 64 result = nil var res = "\"" for i in countup(0, len(s) - 1): if (i + 1) mod MaxLineLength == 0: add(res, '\"') add(res, tnl) app(result, toRope(res)) # reset: setlen(res, 1) res[0] = '\"' add(res, toCChar(s[i])) add(res, '\"') app(result, toRope(res)) proc makeLLVMString*(s: string): PRope = const MaxLineLength = 64 result = nil var res = "c\"" for i in countup(0, len(s) - 1): if (i + 1) mod MaxLineLength == 0: app(result, toRope(res)) setlen(res, 0) case s[i] of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\': add(res, '\\') add(res, toHex(ord(s[i]), 2)) else: add(res, s[i]) add(res, "\\00\"") app(result, toRope(res)) InitTypeTables()