# # # The Nim Compiler # (c) Copyright 2018 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # included from cgen.nim ## This include file contains the logic to produce constant string ## and seq literals. The code here is responsible that ## ``const x = ["a", "b"]`` works without hidden runtime creation code. ## The price is that seqs and strings are not purely a library ## implementation. template detectVersion(field, corename) = if m.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc, gcHooks}: result = 2 else: result = 1 proc detectStrVersion(m: BModule): int = if m.g.config.usesSso() and m.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc, gcHooks}: result = 3 else: detectVersion(strVersion, "nimStrVersion") proc detectSeqVersion(m: BModule): int = detectVersion(seqVersion, "nimSeqVersion") # ----- Version 1: GC'ed strings and seqs -------------------------------- proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) = cgsym(m, "TGenericSeq") let tmp = getTempName(m) result.add tmp var res = newBuilder("") res.addVarWithTypeAndInitializer(AlwaysConst, name = tmp): res.addSimpleStruct(m, name = "", baseType = ""): res.addField(name = "Sup", typ = "TGenericSeq") res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1) do: var strInit: StructInitializer res.addStructInitializer(strInit, kind = siOrderedStruct): res.addField(strInit, name = "Sup"): var seqInit: StructInitializer res.addStructInitializer(seqInit, kind = siOrderedStruct): res.addField(seqInit, name = "len"): res.addIntValue(s.len) res.addField(seqInit, name = "reserved"): res.add(cCast(NimInt, cOp(BitOr, NimUint, cCast(NimUint, cIntValue(s.len)), NimStrlitFlag))) res.addField(strInit, name = "data"): res.add(makeCString(s)) m.s[cfsStrData].add(extract(res)) proc genStringLiteralV1(m: BModule; n: PNode; result: var Builder) = if s.isNil: result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), NimNil)) else: let id = nodeTableTestOrSet(m.dataCache, n, m.labels) var name: string = "" if id == m.labels: # string literal not found in the cache: genStringLiteralDataOnlyV1(m, n.strVal, name) else: name = m.tmpBase & $id result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), cAddr(name))) # ------ Version 2: destructor based strings and seqs ----------------------- proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) = var res = newBuilder("") res.addVarWithTypeAndInitializer( if isConst: AlwaysConst else: Global, name = result): res.addSimpleStruct(m, name = "", baseType = ""): res.addField(name = "cap", typ = NimInt) res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1) do: var structInit: StructInitializer res.addStructInitializer(structInit, kind = siOrderedStruct): res.addField(structInit, name = "cap"): res.add(cOp(BitOr, NimInt, cIntValue(s.len), NimStrlitFlag)) res.addField(structInit, name = "data"): res.add(makeCString(s)) m.s[cfsStrData].add(extract(res)) proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Builder) = let id = nodeTableTestOrSet(m.dataCache, n, m.labels) var litName: string if id == m.labels: cgsym(m, "NimStrPayload") cgsym(m, "NimStringV2") # string literal not found in the cache: litName = getTempName(m) genStringLiteralDataOnlyV2(m, n.strVal, litName, isConst) else: litName = m.tmpBase & $id let tmp = getTempName(m) result.add tmp var res = newBuilder("") res.addVarWithInitializer( if isConst: AlwaysConst else: Global, name = tmp, typ = "NimStringV2"): var strInit: StructInitializer res.addStructInitializer(strInit, kind = siOrderedStruct): res.addField(strInit, name = "len"): res.addIntValue(n.strVal.len) res.addField(strInit, name = "p"): res.add(cCast(ptrType("NimStrPayload"), cAddr(litName))) m.s[cfsStrData].add(extract(res)) proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Builder) = let id = nodeTableTestOrSet(m.dataCache, n, m.labels) var pureLit: Rope if id == m.labels: pureLit = getTempName(m) cgsym(m, "NimStrPayload") cgsym(m, "NimStringV2") # string literal not found in the cache: genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst) else: pureLit = m.tmpBase & rope(id) var strInit: StructInitializer result.addStructInitializer(strInit, kind = siOrderedStruct): result.addField(strInit, name = "len"): result.addIntValue(n.strVal.len) result.addField(strInit, name = "p"): result.add(cCast(ptrType("NimStrPayload"), cAddr(pureLit))) proc ssoCharLit(ch: char): string = ## Return a C char literal for ch, with proper escaping. const hexDigits = "0123456789abcdef" result = "'" case ch of '\'': result.add("\\'") of '\\': result.add("\\\\") of '\0': result.add("\\0") of '\n': result.add("\\n") of '\r': result.add("\\r") of '\t': result.add("\\t") elif ch.ord < 32 or ch.ord == 127: result.add("\\x") result.add(hexDigits[ch.ord shr 4]) result.add(hexDigits[ch.ord and 0xf]) else: result.add(ch) result.add('\'') proc ssoBytesLit(m: BModule; s: string; slen: int): string = ## Compute the `bytes` field value for the new SmallString layout. ## byte 0 = slen, bytes 1-7 = inline chars 0-6 (zero-padded). ## On LE: slen in bits 0-7, char[i] in bits (i+1)*8..(i+1)*8+7. ## On BE: slen in bits 56-63, char[i] in bits (6-i)*8..(6-i)*8+7. const AlwaysAvail = 7 var val: uint64 if CPU[m.g.config.target.targetCPU].endian == littleEndian: val = uint64(slen) for i in 0..