summary refs log blame commit diff stats
path: root/compiler/ccgutils.nim
blob: ad7f22bbccfd392c39bfe2f987a6abb844358330 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12


                               
                                         







                                                             
                                                                       
                 






































                                                           
 


                                          


                                                                               




                                                       
  
                                         
                                      
                        
                  
        
                      
                      




                                                                       


                               
                  

                                  
                                                                      
                                        
                                                       
                                                   
                                                                    




                                                                       

                                                               
                                                             
                                                   
                                              
                                               

                                       





















                                                              

                                                                     











                                                               
                                                         





                                                               
                                    



                                
                                 



                                                             





                                                
  
                                      




                                                                    
              
                

                                      
                    
                   






                                       

                                         
              
                 

                                       









                                                 
                
#
#
#           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()