12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194 |
- ;;; Guile bytecode assembler
- ;;; Copyright (C) 2001, 2009-2023 Free Software Foundation, Inc.
- ;;;
- ;;; This library is free software; you can redistribute it and/or
- ;;; modify it under the terms of the GNU Lesser General Public
- ;;; License as published by the Free Software Foundation; either
- ;;; version 3 of the License, or (at your option) any later version.
- ;;;
- ;;; This library is distributed in the hope that it will be useful,
- ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- ;;; Lesser General Public License for more details.
- ;;;
- ;;; You should have received a copy of the GNU Lesser General Public
- ;;; License along with this library; if not, write to the Free Software
- ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- ;;; Commentary:
- ;;;
- ;;; This module implements an assembler that creates an ELF image from
- ;;; bytecode assembly and macro-assembly. The input can be given in
- ;;; s-expression form, like ((OP ARG ...) ...). Internally there is a
- ;;; procedural interface, the emit-OP procedures, but that is not
- ;;; currently exported.
- ;;;
- ;;; "Primitive instructions" correspond to VM operations. Assemblers
- ;;; for primitive instructions are generated programmatically from
- ;;; (instruction-list), which itself is derived from the VM sources.
- ;;; There are also "macro-instructions" like "label" or "load-constant"
- ;;; that expand to 0 or more primitive instructions.
- ;;;
- ;;; The assembler also handles some higher-level tasks, like creating
- ;;; the symbol table, other metadata sections, creating a constant table
- ;;; for the whole compilation unit, and writing the dynamic section of
- ;;; the ELF file along with the appropriate initialization routines.
- ;;;
- ;;; Most compilers will want to use the trio of make-assembler,
- ;;; emit-text, and link-assembly. That will result in the creation of
- ;;; an ELF image as a bytevector, which can then be loaded using
- ;;; load-thunk-from-memory, or written to disk as a .go file.
- ;;;
- ;;; Code:
- (define-module (system vm assembler)
- #:use-module (system base target)
- #:use-module (system base types internal)
- #:use-module (system vm dwarf)
- #:use-module (system vm elf)
- #:use-module (system vm linker)
- #:use-module (system syntax internal)
- #:use-module (language bytecode)
- #:use-module (rnrs bytevectors)
- #:use-module (ice-9 binary-ports)
- #:use-module (ice-9 vlist)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-11)
- #:export (make-assembler
- (emit-receive* . emit-receive)
- (emit-mov* . emit-mov)
- (emit-fmov* . emit-fmov)
- emit-u64=?
- emit-u64<?
- emit-u64-imm<?
- emit-imm-u64<?
- emit-s64-imm=?
- emit-s64<?
- emit-s64-imm<?
- emit-imm-s64<?
- emit-f64=?
- emit-f64<?
- emit-=?
- emit-<?
- emit-arguments<=?
- emit-positional-arguments<=?
- emit-immediate-tag=?
- emit-heap-tag=?
- emit-eq?
- emit-eq-immediate?
- emit-heap-numbers-equal?
- emit-j
- emit-jl
- emit-je
- emit-jnl
- emit-jne
- emit-jge
- emit-jnge
- emit-jtable
- emit-fixnum?
- emit-heap-object?
- emit-char?
- emit-undefined?
- emit-null?
- emit-false?
- emit-nil?
- emit-untag-fixnum
- emit-tag-fixnum
- emit-untag-char
- emit-tag-char
- emit-s64->f64
- emit-throw
- (emit-throw/value* . emit-throw/value)
- (emit-throw/value+data* . emit-throw/value+data)
- emit-unreachable
- emit-pair?
- emit-struct?
- emit-symbol?
- emit-variable?
- emit-vector?
- emit-mutable-vector?
- emit-immutable-vector?
- emit-weak-vector?
- emit-string?
- emit-heap-number?
- emit-hash-table?
- emit-pointer?
- emit-fluid?
- emit-stringbuf?
- emit-dynamic-state?
- emit-frame?
- emit-keyword?
- emit-syntax?
- emit-program?
- emit-vm-continuation?
- emit-bytevector?
- emit-weak-set?
- emit-weak-table?
- emit-array?
- emit-bitvector?
- emit-port?
- emit-smob?
- emit-bignum?
- emit-flonum?
- emit-compnum?
- emit-fracnum?
- emit-allocate-words
- emit-allocate-words/immediate
- emit-allocate-pointerless-words
- emit-allocate-pointerless-words/immediate
- emit-scm-ref
- emit-scm-set!
- emit-scm-ref/tag
- emit-scm-set!/tag
- emit-scm-ref/immediate
- emit-scm-set!/immediate
- emit-word-ref
- emit-word-set!
- emit-word-ref/immediate
- emit-word-set!/immediate
- emit-pointer-ref/immediate
- emit-pointer-set!/immediate
- emit-tail-pointer-ref/immediate
- emit-u8-ref
- emit-s8-ref
- emit-u16-ref
- emit-s16-ref
- emit-u32-ref
- emit-s32-ref
- emit-u64-ref
- emit-s64-ref
- emit-f32-ref
- emit-f64-ref
- emit-u8-set!
- emit-s8-set!
- emit-u16-set!
- emit-s16-set!
- emit-u32-set!
- emit-s32-set!
- emit-u64-set!
- emit-s64-set!
- emit-f32-set!
- emit-f64-set!
- emit-atomic-scm-ref/immediate
- emit-atomic-scm-set!/immediate
- emit-atomic-scm-swap!/immediate
- emit-atomic-scm-compare-and-swap!/immediate
- ;; Intrinsics.
- emit-add
- emit-add/immediate
- emit-sub
- emit-sub/immediate
- emit-mul
- emit-div
- emit-quo
- emit-rem
- emit-mod
- emit-inexact
- emit-abs
- emit-sqrt
- emit-floor
- emit-ceiling
- emit-sin
- emit-cos
- emit-tan
- emit-asin
- emit-acos
- emit-atan
- emit-atan2
- emit-fabs
- emit-fsqrt
- emit-ffloor
- emit-fceiling
- emit-fsin
- emit-fcos
- emit-ftan
- emit-fasin
- emit-facos
- emit-fatan
- emit-fatan2
- emit-logand
- emit-logior
- emit-logxor
- emit-logsub
- emit-string-set!
- emit-string->number
- emit-string->symbol
- emit-symbol->keyword
- emit-class-of
- emit-scm->f64
- emit-scm->u64
- emit-scm->u64/truncate
- emit-scm->s64
- emit-u64->scm
- emit-s64->scm
- emit-wind
- emit-unwind
- emit-push-fluid
- emit-pop-fluid
- emit-fluid-ref
- emit-fluid-set!
- emit-push-dynamic-state
- emit-pop-dynamic-state
- emit-lsh
- emit-rsh
- emit-lsh/immediate
- emit-rsh/immediate
- emit-resolve-module
- emit-module-variable
- emit-lookup
- emit-lookup-bound
- emit-lookup-bound-public
- emit-lookup-bound-private
- emit-define!
- emit-current-module
- ;; Intrinsics for use by the baseline compiler.
- emit-$car
- emit-$cdr
- emit-$set-car!
- emit-$set-cdr!
- emit-$variable-ref
- emit-$variable-set!
- emit-$vector-length
- emit-$vector-ref
- emit-$vector-set!
- emit-$vector-ref/immediate
- emit-$vector-set!/immediate
- emit-$allocate-struct
- emit-$struct-vtable
- emit-$struct-ref
- emit-$struct-set!
- emit-$struct-ref/immediate
- emit-$struct-set!/immediate
- emit-cache-ref
- emit-cache-set!
- emit-call
- emit-call-label
- emit-tail-call
- emit-tail-call-label
- (emit-instrument-entry* . emit-instrument-entry)
- (emit-instrument-loop* . emit-instrument-loop)
- emit-receive-values
- emit-return-values
- emit-shuffle-down
- emit-call/cc
- emit-abort
- emit-builtin-ref
- emit-assert-nargs-ee
- emit-assert-nargs-ge
- emit-assert-nargs-le
- emit-reset-frame
- emit-assert-nargs-ee/locals
- emit-bind-kwargs
- emit-bind-rest
- emit-load-label
- emit-resolve
- emit-prompt
- emit-current-thread
- emit-fadd
- emit-fsub
- emit-fmul
- emit-fdiv
- emit-uadd
- emit-usub
- emit-umul
- emit-uadd/immediate
- emit-usub/immediate
- emit-umul/immediate
- emit-ulogand
- emit-ulogior
- emit-ulogxor
- emit-ulogsub
- emit-ursh
- emit-srsh
- emit-ulsh
- emit-ursh/immediate
- emit-srsh/immediate
- emit-ulsh/immediate
- emit-make-array
- emit-load-f64
- emit-load-u64
- emit-load-s64
- emit-handle-interrupts
- emit-text
- link-assembly))
- ;; Like define-inlinable, but only for first-order uses of the defined
- ;; routine. Should residualize less code.
- (eval-when (expand)
- (define-syntax define-inline
- (lambda (x)
- (syntax-case x ()
- ((_ (name arg ...) body ...)
- (with-syntax (((temp ...) (generate-temporaries #'(arg ...))))
- #`(eval-when (expand)
- (define-syntax-rule (name temp ...)
- (let ((arg temp) ...)
- body ...)))))))))
- ;;; Bytecode consists of 32-bit units, often subdivided in some way.
- ;;; These helpers create one 32-bit unit from multiple components.
- (define-inline (check-urange x mask)
- (unless (and (exact-integer? x) (= x (logand x mask)))
- (error "out of range" x))
- x)
- (define-inline (check-srange x mask)
- (let ((x* (logand x mask)))
- (unless (if (negative? x)
- (= (+ x mask 1) x*)
- (= x x*))
- (error "out of range" x))
- x*))
- (define-inline (pack-u8-u24 x y)
- (let ((x (check-urange x #xff))
- (y (check-urange y #xffffff)))
- (logior x (ash y 8))))
- (define-inline (pack-u8-s24 x y)
- (let ((x (check-urange x #xff))
- (y (check-srange y #xffffff)))
- (logior x (ash y 8))))
- (define-inline (pack-u16-u16 x y)
- (let ((x (check-urange x #xffff))
- (y (check-urange y #xffff)))
- (logior x (ash y 16))))
- (define-inline (pack-u1-u7-u24 x y z)
- (let ((x (check-urange x #x1))
- (y (check-urange y #x7f))
- (z (check-urange z #xffffff)))
- (logior x (ash y 1) (ash z 8))))
- (define-inline (pack-u8-u12-u12 x y z)
- (let ((x (check-urange x #xff))
- (y (check-urange y #xfff))
- (z (check-urange z #xfff)))
- (logior x (ash y 8) (ash z 20))))
- (define-inline (pack-u8-u12-s12 x y z)
- (let ((x (check-urange x #xff))
- (y (check-urange y #xfff))
- (z (check-srange z #xfff)))
- (logior x (ash y 8) (ash z 20))))
- (define-inline (pack-u8-u8-u16 x y z)
- (let ((x (check-urange x #xff))
- (y (check-urange y #xff))
- (z (check-urange z #xffff)))
- (logior x (ash y 8) (ash z 16))))
- (define-inline (pack-u8-u8-u8-u8 x y z w)
- (let ((x (check-urange x #xff))
- (y (check-urange y #xff))
- (z (check-urange z #xff))
- (w (check-urange w #xff)))
- (logior x (ash y 8) (ash z 16) (ash w 24))))
- (eval-when (expand)
- (define-syntax pack-flags
- (syntax-rules ()
- ;; Add clauses as needed.
- ((pack-flags f1 f2) (logior (if f1 (ash 1 0) 0)
- (if f2 (ash 1 1) 0))))))
- (define-syntax-rule (define-byte-order-swapper name size ref set)
- (define* (name buf #:optional (start 0) (end (bytevector-length buf)))
- "Patch up the text buffer @var{buf}, swapping the endianness of each
- N-byte unit."
- (unless (zero? (modulo (- end start) size))
- (error "unexpected length"))
- (let lp ((pos start))
- (when (< pos end)
- (set buf pos (ref buf pos (endianness big)) (endianness little))
- (lp (+ pos size))))))
- (define-byte-order-swapper byte-swap/2!
- 2 bytevector-u16-ref bytevector-u16-set!)
- (define-byte-order-swapper byte-swap/4!
- 4 bytevector-u32-ref bytevector-u32-set!)
- (define-byte-order-swapper byte-swap/8!
- 8 bytevector-u64-ref bytevector-u64-set!)
- ;;; A <meta> entry collects metadata for one procedure. Procedures are
- ;;; written as contiguous ranges of bytecode.
- ;;;
- (eval-when (expand)
- (define-syntax-rule (assert-match arg pattern kind)
- (let ((x arg))
- (unless (match x (pattern #t) (_ #f))
- (error (string-append "expected " kind) x)))))
- (define-record-type <jit-data>
- (make-jit-data low-pc high-pc)
- jit-data?
- (low-pc jit-data-low-pc)
- (high-pc jit-data-high-pc))
- (define-record-type <meta>
- (%make-meta label properties low-pc high-pc arities jit-data-label)
- meta?
- (label meta-label)
- (properties meta-properties set-meta-properties!)
- (low-pc meta-low-pc)
- (high-pc meta-high-pc set-meta-high-pc!)
- (arities meta-arities set-meta-arities!)
- (jit-data-label meta-jit-data-label))
- (define (make-meta label properties low-pc)
- (assert-match label (or (? exact-integer?) (? symbol?)) "symbol")
- (assert-match properties (((? symbol?) . _) ...) "alist with symbolic keys")
- (%make-meta label properties low-pc #f '() (gensym "jit-data")))
- (define (meta-name meta)
- (assq-ref (meta-properties meta) 'name))
- ;; Metadata for one <lambda-case>.
- (define-record-type <arity>
- (make-arity req opt rest kw-indices allow-other-keys? has-closure?
- low-pc high-pc definitions)
- arity?
- (req arity-req)
- (opt arity-opt)
- (rest arity-rest)
- (kw-indices arity-kw-indices)
- (allow-other-keys? arity-allow-other-keys?)
- (has-closure? arity-has-closure?)
- (low-pc arity-low-pc)
- (high-pc arity-high-pc set-arity-high-pc!)
- (definitions arity-definitions set-arity-definitions!))
- ;;; An assembler collects all of the words emitted during assembly, and
- ;;; also maintains ancillary information such as the constant table, a
- ;;; relocation list, and so on.
- ;;;
- ;;; Bytecode consists of 32-bit units. We emit bytecode using native
- ;;; endianness. If we're targeting a foreign endianness, we byte-swap
- ;;; the bytevector as a whole instead of conditionalizing each access.
- ;;;
- (define-record-type <asm>
- (make-asm buf pos start
- labels relocs
- word-size endianness
- constants inits
- shstrtab next-section-number
- meta sources
- slot-maps)
- asm?
- ;; We write bytecode into a bytevector, growing the bytevector as
- ;; needed. asm-cur is that bytevector, and asm-pos is the byte offset
- ;; into the vector at which the next word should be written.
- ;;
- (buf asm-buf set-asm-buf!)
- (pos asm-pos set-asm-pos!)
- ;; asm-start is an absolute position, indicating the byte offset of
- ;; the beginning of an instruction. It is updated after writing all
- ;; the words for one primitive instruction. It models the position of
- ;; the instruction pointer during execution, given that the VM updates
- ;; the IP only at the end of executing the instruction, and is thus
- ;; useful for computing offsets between two points in a program.
- ;;
- (start asm-start set-asm-start!)
- ;; An alist of symbol -> position pairs, indicating the labels defined
- ;; in this compilation unit.
- ;;
- (labels asm-labels set-asm-labels!)
- ;; A list of relocations needed by the program text. We use an
- ;; internal representation for relocations, and handle textual
- ;; relative relocations in the assembler. Other kinds of relocations
- ;; are later reified as linker relocations and resolved by the linker.
- ;;
- (relocs asm-relocs set-asm-relocs!)
- ;; Target information.
- ;;
- (word-size asm-word-size)
- (endianness asm-endianness)
- ;; The constant table, as a vhash of object -> label. All constants
- ;; get de-duplicated and written into separate sections -- either the
- ;; .rodata section, for read-only data, or .data, for constants that
- ;; need initialization at load-time (like symbols). Constants can
- ;; depend on other constants (e.g. a symbol depending on a stringbuf),
- ;; so order in this table is important.
- ;;
- (constants asm-constants set-asm-constants!)
- ;; A vhash of label to init descriptors, where an init descriptor is
- ;; #(EMIT-INIT STATIC? PATCHES). EMIT-INIT, if present, is a
- ;; procedure taking the asm and the label as arguments. Unless the
- ;; object is statically allocatable, in which case it can be loaded
- ;; via make-non-immediate rather than static-ref, EMIT-INIT should
- ;; also initialize the corresponding cell for any later static-ref.
- ;; If STATIC? is true, the value can be loaded with
- ;; emit-make-non-immediate, otherwise it's emit-static-ref. A bit
- ;; confusing but that's how it is. PATCHES is a list of (DEST-LABEL
- ;; . FIELD) pairs, indicating locations to which to patch the value.
- ;; Like asm-constants, order is important.
- ;;
- (inits asm-inits set-asm-inits!)
- ;; The shstrtab, for section names.
- ;;
- (shstrtab asm-shstrtab set-asm-shstrtab!)
- ;; The section number for the next section to be written.
- ;;
- (next-section-number asm-next-section-number set-asm-next-section-number!)
- ;; A list of <meta>, corresponding to procedure metadata.
- ;;
- (meta asm-meta set-asm-meta!)
- ;; A list of (pos . source) pairs, indicating source information. POS
- ;; is relative to the beginning of the text section, and SOURCE is in
- ;; the same format that source-properties returns.
- ;;
- (sources asm-sources set-asm-sources!)
- ;; A list of (pos . slot-map) pairs, indicating slot maps. POS is
- ;; relative to the beginning of the text section. SLOT-MAP is a
- ;; bitfield describing the stack at call sites, as an integer.
- ;;
- (slot-maps asm-slot-maps set-asm-slot-maps!))
- (define* (make-assembler #:key (word-size (target-word-size))
- (endianness (target-endianness)))
- "Create an assembler for a given target @var{word-size} and
- @var{endianness}, falling back to appropriate values for the configured
- target."
- (make-asm (make-u32vector 1000) 0 0
- (make-hash-table) '()
- word-size endianness
- vlist-null vlist-null
- (make-string-table) 1
- '() '() '()))
- (define (intern-section-name! asm string)
- "Add a string to the section name table (shstrtab)."
- (string-table-intern! (asm-shstrtab asm) string))
- (define (grow-buffer! asm)
- "Grow the code buffer of the asm."
- (let* ((buf (asm-buf asm))
- (len (bytevector-length buf))
- (new (make-u32vector (ash len -1) 0)))
- (bytevector-copy! buf 0 new 0 len)
- (set-asm-buf! asm new)
- #f))
- (define-inline (emit asm u32)
- "Emit one 32-bit word into the instruction stream. Assumes that there
- is space for the word."
- (bytevector-u32-native-set! (asm-buf asm) (asm-pos asm) u32)
- (set-asm-pos! asm (+ (asm-pos asm) 4)))
- (define-inline (make-reloc type label base word)
- "Make an internal relocation of type @var{type} referencing symbol
- @var{label}, @var{word} words after position @var{start}. @var{type}
- may be x8-s24, indicating a 24-bit relative label reference that can be
- fixed up by the assembler, or s32, indicating a 32-bit relative
- reference that needs to be fixed up by the linker."
- (list type label base word))
- (define-inline (reset-asm-start! asm)
- "Reset the asm-start after writing the words for one instruction."
- (set-asm-start! asm (asm-pos asm)))
- (define (record-label-reference asm label)
- "Record an x8-s24 local label reference. This value will get patched
- up later by the assembler."
- (let* ((start (asm-start asm))
- (pos (asm-pos asm))
- (reloc (make-reloc 'x8-s24 label start (- pos start))))
- (set-asm-relocs! asm (cons reloc (asm-relocs asm)))))
- (define* (record-far-label-reference asm label #:optional (offset 0))
- "Record an s32 far label reference. This value will get patched up
- later by the linker."
- (let* ((start (- (asm-start asm) offset))
- (pos (asm-pos asm))
- (reloc (make-reloc 's32 label start (- pos start))))
- (set-asm-relocs! asm (cons reloc (asm-relocs asm)))))
- (define (immediate-bits asm x)
- (let ((bits (scm->immediate-bits x)))
- (and bits (truncate-bits bits (* 8 (asm-word-size asm)) #t))))
- ;;;
- ;;; Primitive assemblers are defined by expanding `assembler' for each
- ;;; opcode in `(instruction-list)'.
- ;;;
- (eval-when (expand)
- (define (id-append ctx a b)
- (datum->syntax ctx (symbol-append (syntax->datum a) (syntax->datum b))))
- (define-syntax encoder
- (lambda (x)
- (define-syntax op-case
- (lambda (x)
- (syntax-case x ()
- ((_ asm name ((type arg ...) code ...) clause ...)
- #`(if (eq? name 'type)
- (with-syntax (((arg ...) (generate-temporaries #'(arg ...))))
- #'((arg ...)
- code ...))
- (op-case asm name clause ...)))
- ((_ asm name)
- #'(error "unmatched name" name)))))
- (define (pack-first-word asm opcode type)
- (with-syntax ((opcode opcode))
- (op-case
- asm type
- ((X32)
- (emit asm opcode))
- ((X8_S24 arg)
- (emit asm (pack-u8-u24 opcode arg)))
- ((X8_F24 arg)
- (emit asm (pack-u8-u24 opcode arg)))
- ((X8_C24 arg)
- (emit asm (pack-u8-u24 opcode arg)))
- ((X8_L24 label)
- (record-label-reference asm label)
- (emit asm opcode))
- ((X8_S8_I16 a imm)
- (let ((bits (truncate-bits (scm->immediate-bits imm) 16 #f)))
- (emit asm (pack-u8-u8-u16 opcode a bits))))
- ((X8_S8_ZI16 a imm)
- (let ((bits (truncate-bits (scm->immediate-bits imm) 16 #t)))
- (emit asm (pack-u8-u8-u16 opcode a bits))))
- ((X8_S12_S12 a b)
- (emit asm (pack-u8-u12-u12 opcode a b)))
- ((X8_S12_C12 a b)
- (emit asm (pack-u8-u12-u12 opcode a b)))
- ((X8_S12_Z12 a b)
- (emit asm (pack-u8-u12-s12 opcode a b)))
- ((X8_C12_C12 a b)
- (emit asm (pack-u8-u12-u12 opcode a b)))
- ((X8_F12_F12 a b)
- (emit asm (pack-u8-u12-u12 opcode a b)))
- ((X8_S8_S8_S8 a b c)
- (emit asm (pack-u8-u8-u8-u8 opcode a b c)))
- ((X8_S8_S8_C8 a b c)
- (emit asm (pack-u8-u8-u8-u8 opcode a b c)))
- ((X8_S8_C8_S8 a b c)
- (emit asm (pack-u8-u8-u8-u8 opcode a b c))))))
- (define (pack-tail-word asm type)
- (op-case
- asm type
- ((C32 a)
- (emit asm a))
- ((I32 imm)
- (let ((val (immediate-bits asm imm)))
- (emit asm val)))
- ((A32 imm)
- (unless (= (asm-word-size asm) 8)
- (error "make-long-immediate unavailable for this target"))
- (let ((bits (immediate-bits asm imm)))
- (emit asm (ash bits -32))
- (emit asm (logand bits (1- (ash 1 32))))))
- ((AF32 f64)
- (let ((u64 (u64vector-ref (f64vector f64) 0)))
- (emit asm (ash u64 -32))
- (emit asm (logand u64 (1- (ash 1 32))))))
- ((AU32 u64)
- (emit asm (ash u64 -32))
- (emit asm (logand u64 (1- (ash 1 32)))))
- ((AS32 s64)
- (let ((u64 (u64vector-ref (s64vector s64) 0)))
- (emit asm (ash u64 -32))
- (emit asm (logand u64 (1- (ash 1 32))))))
- ((B32))
- ((BU32))
- ((BS32))
- ((BF32))
- ((N32 label)
- (record-far-label-reference asm label)
- (emit asm 0))
- ((R32 label)
- (record-far-label-reference asm label)
- (emit asm 0))
- ((L32 label)
- (record-far-label-reference asm label)
- (emit asm 0))
- ((LO32 label offset)
- (record-far-label-reference asm label
- (* offset (asm-word-size asm)))
- (emit asm 0))
- ((C8_C24 a b)
- (emit asm (pack-u8-u24 a b)))
- ((C8_S24 a b)
- (emit asm (pack-u8-u24 a b)))
- ((C16_C16 a b)
- (emit asm (pack-u16-u16 a b)))
- ((V32_X8_L24 labels)
- (let ((len (vector-length labels)))
- (emit asm len)
- (let lp ()
- (unless (<= (+ (asm-pos asm) (* 4 len))
- (bytevector-length (asm-buf asm)))
- (grow-buffer! asm)
- (lp)))
- (let lp ((n 0))
- (when (< n len)
- (record-label-reference asm (vector-ref labels n))
- (emit asm 0)
- (lp (1+ n))))))
- ((B1_X7_L24 a label)
- (record-label-reference asm label)
- (emit asm (pack-u1-u7-u24 (if a 1 0) 0 0)))
- ((B1_C7_L24 a b label)
- (record-label-reference asm label)
- (emit asm (pack-u1-u7-u24 (if a 1 0) b 0)))
- ((B1_X31 a)
- (emit asm (pack-u1-u7-u24 (if a 1 0) 0 0)))
- ((B1_X7_S24 a b)
- (emit asm (pack-u1-u7-u24 (if a 1 0) 0 b)))
- ((B1_X7_F24 a b)
- (emit asm (pack-u1-u7-u24 (if a 1 0) 0 b)))
- ((B1_X7_C24 a b)
- (emit asm (pack-u1-u7-u24 (if a 1 0) 0 b)))
- ((X8_S24 a)
- (emit asm (pack-u8-u24 0 a)))
- ((X8_F24 a)
- (emit asm (pack-u8-u24 0 a)))
- ((X8_C24 a)
- (emit asm (pack-u8-u24 0 a)))
- ((X8_L24 label)
- (record-label-reference asm label)
- (emit asm 0))))
- (syntax-case x ()
- ((_ word0 word* ...)
- (with-syntax ((((formal0 ...)
- code0 ...)
- (pack-first-word #'asm #'opcode
- (syntax->datum #'word0)))
- ((((formal* ...)
- code* ...) ...)
- (map (lambda (word) (pack-tail-word #'asm word))
- (syntax->datum #'(word* ...)))))
- ;; The opcode is the last argument, so that assemblers don't
- ;; have to shuffle their arguments before tail-calling an
- ;; encoder.
- #'(lambda (asm formal0 ... formal* ... ... opcode)
- (let lp ()
- (let ((words (length '(word0 word* ...))))
- (unless (<= (+ (asm-pos asm) (* 4 words))
- (bytevector-length (asm-buf asm)))
- (grow-buffer! asm)
- (lp))))
- code0 ...
- code* ... ...
- (reset-asm-start! asm)))))))
- (define (encoder-name operands)
- (let lp ((operands operands) (out #'encode))
- (syntax-case operands ()
- (() out)
- ((operand . operands)
- (lp #'operands
- (id-append #'operand (id-append out out #'-) #'operand))))))
- (define-syntax define-encoder
- (lambda (x)
- (syntax-case x ()
- ((_ operand ...)
- (with-syntax ((encode (encoder-name #'(operand ...))))
- #'(define encode (encoder operand ...)))))))
- (define-syntax visit-instruction-kinds
- (lambda (x)
- (syntax-case x ()
- ((visit-instruction-kinds macro arg ...)
- (with-syntax (((operands ...)
- (delete-duplicates
- (map (match-lambda
- ((name opcode kind . operands)
- (datum->syntax #'macro operands)))
- (instruction-list)))))
- #'(begin
- (macro arg ... . operands)
- ...)))))))
- (visit-instruction-kinds define-encoder)
- ;; In Guile's VM, locals are usually addressed via the stack pointer
- ;; (SP). There can be up to 2^24 slots for local variables in a
- ;; frame. Some instructions encode their operands using a restricted
- ;; subset of the full 24-bit local address space, in order to make the
- ;; bytecode more dense in the usual case that a function needs few
- ;; local slots. To allow these instructions to be used when there are
- ;; many local slots, we can temporarily push the values on the stack,
- ;; operate on them there, and then store back any result as we pop the
- ;; SP to its original position.
- ;;
- ;; We implement this shuffling via wrapper encoders that have the same
- ;; arity as the encoder they wrap, e.g. encode-X8_S12_S12/shuffle that
- ;; wraps encode-X8_S12_S12. We make the emit-cons public interface
- ;; use the shuffling encoder. That way we solve the problem fully and
- ;; in just one place.
- (define (encode-X8_S12_S12!/shuffle asm a b opcode)
- (cond
- ((< (logior a b) (ash 1 12))
- (encode-X8_S12_S12 asm a b opcode))
- (else
- (emit-push asm a)
- (emit-push asm (1+ b))
- (encode-X8_S12_S12 asm 1 0 opcode)
- (emit-drop asm 2))))
- (define (encode-X8_S12_S12<-/shuffle asm dst a opcode)
- (cond
- ((< (logior dst a) (ash 1 12))
- (encode-X8_S12_S12 asm dst a opcode))
- (else
- (emit-push asm a)
- (encode-X8_S12_S12 asm 0 0 opcode)
- (emit-pop asm dst))))
- (define (encode-X8_S12_C12!/shuffle asm a const opcode)
- (cond
- ((< a (ash 1 12))
- (encode-X8_S12_C12 asm a const opcode))
- (else
- (emit-push asm a)
- (encode-X8_S12_C12 asm 0 const opcode)
- (emit-drop asm 1))))
- (define (encode-X8_S12_C12<-/shuffle asm dst const opcode)
- (cond
- ((< dst (ash 1 12))
- (encode-X8_S12_C12 asm dst const opcode))
- (else
- ;; Push garbage value to make space for dst.
- (emit-push asm dst)
- (encode-X8_S12_C12 asm 0 const opcode)
- (emit-pop asm dst))))
- (define (encode-X8_S12_Z12!/shuffle asm a const opcode)
- (cond
- ((< a (ash 1 12))
- (encode-X8_S12_Z12 asm a const opcode))
- (else
- (emit-push asm a)
- (encode-X8_S12_Z12 asm 0 const opcode)
- (emit-drop asm 1))))
- (define (encode-X8_S8_I16<-/shuffle asm dst imm opcode)
- (cond
- ((< dst (ash 1 8))
- (encode-X8_S8_I16 asm dst imm opcode))
- (else
- ;; Push garbage value to make space for dst.
- (emit-push asm dst)
- (encode-X8_S8_I16 asm 0 imm opcode)
- (emit-pop asm dst))))
- (define (encode-X8_S8_ZI16!/shuffle asm a imm opcode)
- (cond
- ((< a (ash 1 8))
- (encode-X8_S8_ZI16 asm a imm opcode))
- (else
- (emit-push asm a)
- (encode-X8_S8_ZI16 asm 0 imm opcode)
- (emit-drop asm 1))))
- (define (encode-X8_S8_ZI16<-/shuffle asm dst imm opcode)
- (cond
- ((< dst (ash 1 8))
- (encode-X8_S8_ZI16 asm dst imm opcode))
- (else
- ;; Push garbage value to make space for dst.
- (emit-push asm dst)
- (encode-X8_S8_ZI16 asm 0 imm opcode)
- (emit-pop asm dst))))
- (define (encode-X8_S8_S8_S8!/shuffle asm a b c opcode)
- (cond
- ((< (logior a b c) (ash 1 8))
- (encode-X8_S8_S8_S8 asm a b c opcode))
- (else
- (emit-push asm a)
- (emit-push asm (+ b 1))
- (emit-push asm (+ c 2))
- (encode-X8_S8_S8_S8 asm 2 1 0 opcode)
- (emit-drop asm 3))))
- (define (encode-X8_S8_S8_S8<-/shuffle asm dst a b opcode)
- (cond
- ((< (logior dst a b) (ash 1 8))
- (encode-X8_S8_S8_S8 asm dst a b opcode))
- (else
- (emit-push asm a)
- (emit-push asm (1+ b))
- (encode-X8_S8_S8_S8 asm 1 1 0 opcode)
- (emit-drop asm 1)
- (emit-pop asm dst))))
- (define (encode-X8_S8_S8_C8<-/shuffle asm dst a const opcode)
- (cond
- ((< (logior dst a) (ash 1 8))
- (encode-X8_S8_S8_C8 asm dst a const opcode))
- (else
- (emit-push asm a)
- (encode-X8_S8_S8_C8 asm 0 0 const opcode)
- (emit-pop asm dst))))
- (define (encode-X8_S8_C8_S8!/shuffle asm a const b opcode)
- (cond
- ((< (logior a b) (ash 1 8))
- (encode-X8_S8_C8_S8 asm a const b opcode))
- (else
- (emit-push asm a)
- (emit-push asm (1+ b))
- (encode-X8_S8_C8_S8 asm 1 const 0 opcode)
- (emit-drop asm 2))))
- (define (encode-X8_S8_C8_S8<-/shuffle asm dst const a opcode)
- (cond
- ((< (logior dst a) (ash 1 8))
- (encode-X8_S8_C8_S8 asm dst const a opcode))
- (else
- (emit-push asm a)
- (encode-X8_S8_C8_S8 asm 0 const 0 opcode)
- (emit-pop asm dst))))
- (define (encode-X8_S8_S8_S8-C32!/shuffle asm a b c const opcode)
- (cond
- ((< (logior a b c) (ash 1 8))
- (encode-X8_S8_S8_S8-C32 asm a b c const opcode))
- (else
- (emit-push asm a)
- (emit-push asm (+ b 1))
- (emit-push asm (+ c 2))
- (encode-X8_S8_S8_S8-C32 asm 2 1 0 const opcode)
- (emit-drop asm 3))))
- (define (encode-X8_S8_S8_S8-C32<-/shuffle asm dst a b c32 opcode)
- (cond
- ((< (logior dst a b) (ash 1 8))
- (encode-X8_S8_S8_S8-C32 asm dst a b c32 opcode))
- (else
- (emit-push asm a)
- (emit-push asm (1+ b))
- (encode-X8_S8_S8_S8-C32 asm 1 1 0 c32 opcode)
- (emit-drop asm 1)
- (emit-pop asm dst))))
- (define (encode-X8_S8_S8_C8-C32<-/shuffle asm dst a const c32 opcode)
- (cond
- ((< (logior dst a) (ash 1 8))
- (encode-X8_S8_S8_C8-C32 asm dst a const c32 opcode))
- (else
- (emit-push asm a)
- (encode-X8_S8_S8_C8-C32 asm 0 0 const c32 opcode)
- (emit-pop asm dst))))
- (define (encode-X8_S8_S8_S8-C32!/shuffle asm a b c c32 opcode)
- (cond
- ((< (logior a b c) (ash 1 8))
- (encode-X8_S8_S8_S8-C32 asm a b c c32 opcode))
- (else
- (emit-push asm a)
- (emit-push asm (+ b 1))
- (emit-push asm (+ c 2))
- (encode-X8_S8_S8_S8-C32 asm 2 1 0 c32 opcode)
- (emit-drop asm 3))))
- (define (encode-X8_S8_C8_S8-C32!/shuffle asm a const b c32 opcode)
- (cond
- ((< (logior a b) (ash 1 8))
- (encode-X8_S8_C8_S8-C32 asm a const b c32 opcode))
- (else
- (emit-push asm a)
- (emit-push asm (+ b 1))
- (encode-X8_S8_C8_S8-C32 asm 1 const 0 c32 opcode)
- (emit-drop asm 2))))
- (define (encode-X8_S12_S12-C32<-/shuffle asm dst src c32 opcode)
- (cond
- ((< (logior dst src) (ash 1 12))
- (encode-X8_S12_S12-C32 asm dst src c32 opcode))
- (else
- (emit-push asm src)
- (encode-X8_S12_S12-C32 asm 0 0 c32 opcode)
- (emit-pop asm dst))))
- (define (encode-X8_S12_S12-C32!/shuffle asm a b c32 opcode)
- (cond
- ((< (logior a b) (ash 1 12))
- (encode-X8_S12_S12-C32 asm a b c32 opcode))
- (else
- (emit-push asm a)
- (emit-push asm b)
- (encode-X8_S12_S12-C32 asm 1 0 c32 opcode)
- (emit-drop asm 2))))
- (eval-when (expand)
- (define (id-append ctx a b)
- (datum->syntax ctx (symbol-append (syntax->datum a) (syntax->datum b))))
- (define (shuffling-encoder-name kind operands)
- (match (cons (syntax->datum kind) (syntax->datum operands))
- (('! 'X8_S12_S12) #'encode-X8_S12_S12!/shuffle)
- (('<- 'X8_S12_S12) #'encode-X8_S12_S12<-/shuffle)
- (('! 'X8_S12_S12 'X8_C24) #'encode-X8_S12_S12-X8_C24!/shuffle)
- (('<- 'X8_S12_S12 'X8_C24) #'encode-X8_S12_S12-X8_C24<-/shuffle)
- (('! 'X8_S12_C12) #'encode-X8_S12_C12!/shuffle)
- (('<- 'X8_S12_C12) #'encode-X8_S12_C12<-/shuffle)
- (('! 'X8_S12_Z12) #'encode-X8_S12_Z12!/shuffle)
- (('<- 'X8_S8_I16) #'encode-X8_S8_I16<-/shuffle)
- (('! 'X8_S8_ZI16) #'encode-X8_S8_ZI16!/shuffle)
- (('<- 'X8_S8_ZI16) #'encode-X8_S8_ZI16<-/shuffle)
- (('! 'X8_S8_S8_S8) #'encode-X8_S8_S8_S8!/shuffle)
- (('<- 'X8_S8_S8_S8) #'encode-X8_S8_S8_S8<-/shuffle)
- (('<- 'X8_S8_S8_C8) #'encode-X8_S8_S8_C8<-/shuffle)
- (('! 'X8_S8_S8_S8 'C32) #'encode-X8_S8_S8_S8-C32!/shuffle)
- (('! 'X8_S8_C8_S8 'C32) #'encode-X8_S8_C8_S8-C32!/shuffle)
- (('<- 'X8_S8_S8_S8 'C32) #'encode-X8_S8_S8_S8-C32<-/shuffle)
- (('<- 'X8_S8_S8_C8 'C32) #'encode-X8_S8_S8_C8-C32<-/shuffle)
- (('<- 'X8_S12_S12 'C32) #'encode-X8_S12_S12-C32<-/shuffle)
- (('! 'X8_S12_S12 'C32) #'encode-X8_S12_S12-C32!/shuffle)
- (('! 'X8_S8_C8_S8) #'encode-X8_S8_C8_S8!/shuffle)
- (('<- 'X8_S8_C8_S8) #'encode-X8_S8_C8_S8<-/shuffle)
- (else (encoder-name operands))))
- (define-syntax assembler
- (lambda (x)
- (define (word-args word)
- (match word
- ('C32 #'(a))
- ('I32 #'(imm))
- ('A32 #'(imm))
- ('AF32 #'(f64))
- ('AU32 #'(u64))
- ('AS32 #'(s64))
- ('B32 #'())
- ('BU32 #'())
- ('BS32 #'())
- ('BF32 #'())
- ('N32 #'(label))
- ('R32 #'(label))
- ('L32 #'(label))
- ('LO32 #'(label offset))
- ('C8_C24 #'(a b))
- ('C8_S24 #'(a b))
- ('C16_C16 #'(a b))
- ('V32_X8_L24 #'(labels))
- ('B1_X7_L24 #'(a label))
- ('B1_C7_L24 #'(a b label))
- ('B1_X31 #'(a))
- ('B1_X7_S24 #'(a b))
- ('B1_X7_F24 #'(a b))
- ('B1_X7_C24 #'(a b))
- ('X8_S24 #'(arg))
- ('X8_F24 #'(arg))
- ('X8_C24 #'(arg))
- ('X8_L24 #'(label))
- ('X8_S8_I16 #'(a imm))
- ('X8_S8_ZI16 #'(a imm))
- ('X8_S12_S12 #'(a b))
- ('X8_S12_C12 #'(a b))
- ('X8_S12_Z12 #'(a b))
- ('X8_C12_C12 #'(a b))
- ('X8_F12_F12 #'(a b))
- ('X8_S8_S8_S8 #'(a b c))
- ('X8_S8_S8_C8 #'(a b c))
- ('X8_S8_C8_S8 #'(a b c))
- ('X32 #'())))
- (syntax-case x ()
- ((_ name opcode kind word ...)
- (with-syntax (((formal ...)
- (generate-temporaries
- (append-map word-args (syntax->datum #'(word ...)))))
- (encode (shuffling-encoder-name #'kind #'(word ...))))
- #'(lambda (asm formal ...)
- (encode asm formal ... opcode))))))))
- (define assemblers (make-hash-table))
- (eval-when (expand)
- (define-syntax define-assembler
- (lambda (x)
- (syntax-case x ()
- ((_ name opcode kind arg ...)
- (with-syntax ((emit (id-append #'name #'emit- #'name)))
- #'(define emit
- (let ((emit (assembler name opcode kind arg ...)))
- (hashq-set! assemblers 'name emit)
- emit)))))))
- (define-syntax visit-opcodes
- (lambda (x)
- (syntax-case x ()
- ((visit-opcodes macro arg ...)
- (with-syntax (((inst ...)
- (map (lambda (x) (datum->syntax #'macro x))
- (instruction-list))))
- #'(begin
- (macro arg ... . inst)
- ...)))))))
- (visit-opcodes define-assembler)
- ;; Shuffling is a general mechanism to get around address space
- ;; limitations for SP-relative variable references. FP-relative
- ;; variables need special support. Also, some instructions like `mov'
- ;; have multiple variations with different addressing limits.
- (define (emit-mov* asm dst src)
- (if (and (< dst (ash 1 12)) (< src (ash 1 12)))
- (emit-mov asm dst src)
- (emit-long-mov asm dst src)))
- (define (emit-fmov* asm dst src)
- (emit-long-fmov asm dst src))
- (define (emit-receive* asm dst proc nlocals)
- (if (and (< dst (ash 1 12)) (< proc (ash 1 12)))
- (emit-receive asm dst proc nlocals)
- (begin
- (emit-receive-values asm proc #t 1)
- (emit-fmov* asm dst (1+ proc))
- (emit-reset-frame asm nlocals))))
- (define (emit-throw/value* asm val param)
- (emit-throw/value asm val (intern-non-immediate asm param)))
- (define (emit-throw/value+data* asm val param)
- (emit-throw/value+data asm val (intern-non-immediate asm param)))
- (define (emit-instrument-entry* asm)
- (let ((meta (car (asm-meta asm))))
- (emit-instrument-entry asm (meta-jit-data-label meta))))
- (define (emit-instrument-loop* asm)
- (let ((meta (car (asm-meta asm))))
- (emit-instrument-loop asm (meta-jit-data-label meta))))
- (define (emit-text asm instructions)
- "Assemble @var{instructions} using the assembler @var{asm}.
- @var{instructions} is a sequence of instructions, expressed as a list of
- lists. This procedure can be called many times before calling
- @code{link-assembly}."
- (for-each (lambda (inst)
- (apply (or (hashq-ref assemblers (car inst))
- (error 'bad-instruction inst))
- asm
- (cdr inst)))
- instructions))
- ;;;
- ;;; The constant table records a topologically sorted set of literal
- ;;; constants used by a program. For example, a pair uses its car and
- ;;; cdr, a string uses its stringbuf, etc.
- ;;;
- ;;; Some things we want to add to the constant table are not actually
- ;;; Scheme objects: for example, stringbufs, cache cells for toplevel
- ;;; references, or cache cells for non-closure procedures. For these we
- ;;; define special record types and add instances of those record types
- ;;; to the table.
- ;;;
- (define-record-type <stringbuf>
- (make-stringbuf string)
- stringbuf?
- (string stringbuf-string))
- (define-record-type <static-procedure>
- (make-static-procedure code)
- static-procedure?
- (code static-procedure-code))
- (define-record-type <uniform-vector-backing-store>
- (make-uniform-vector-backing-store bytes element-size)
- uniform-vector-backing-store?
- (bytes uniform-vector-backing-store-bytes)
- (element-size uniform-vector-backing-store-element-size))
- (define-record-type <cache-cell>
- (make-cache-cell key)
- cache-cell?
- (key cache-cell-key))
- (define (simple-vector? obj)
- (and (vector? obj)
- (equal? (array-shape obj) (list (list 0 (1- (vector-length obj)))))))
- (define (simple-uniform-vector? obj)
- (and (array? obj)
- (symbol? (array-type obj))
- (match (array-shape obj)
- (((0 n)) #t)
- (else #f))))
- (define (statically-allocatable? x)
- "Return @code{#t} if a non-immediate constant can be allocated
- statically, and @code{#f} if it would need some kind of runtime
- allocation."
- (or (pair? x) (string? x) (stringbuf? x) (static-procedure? x)
- (array? x) (syntax? x)))
- (define (intern-constant asm obj)
- "Add an object to the constant table, and return a label that can be
- used to reference it. If the object is already present in the constant
- table, its existing label is used directly."
- (define (recur obj)
- (intern-constant asm obj))
- (define (add-desc! label desc)
- (set-asm-inits! asm (vhash-consq label desc (asm-inits asm))))
- (define (init-descriptor obj)
- (let ((label (recur obj)))
- (cond
- ((not label) #f)
- ((vhash-assq label (asm-inits asm)) => cdr)
- (else
- (let ((desc (vector #f #t '())))
- (add-desc! label desc)
- desc)))))
- (define (add-patch! dst field obj)
- (match (init-descriptor obj)
- (#f #f)
- ((and desc #(emit-init emit-load patches))
- (vector-set! desc 2 (acons dst field patches)))))
- (define (add-init! dst init)
- (add-desc! dst (vector init #f '())))
- (define (intern! obj label)
- (define (patch! field obj) (add-patch! label field obj))
- (define (init! emit-init) (add-init! label emit-init))
- (cond
- ((pair? obj)
- (patch! 0 (car obj))
- (patch! 1 (cdr obj)))
- ((simple-vector? obj)
- (let lp ((i 0))
- (when (< i (vector-length obj))
- (patch! (1+ i) (vector-ref obj i))
- (lp (1+ i)))))
- ((syntax? obj)
- (patch! 1 (syntax-expression obj))
- (patch! 2 (syntax-wrap obj))
- (patch! 3 (syntax-module obj))
- (patch! 4 (syntax-sourcev obj)))
- ((stringbuf? obj))
- ((static-procedure? obj)
- ;; Special case, as we can't load the procedure's code using
- ;; make-non-immediate.
- (let* ((code (static-procedure-code obj))
- (init (lambda (asm label)
- (emit-static-patch! asm label 1 code)
- #f)))
- (add-desc! label (vector init #t '()))))
- ((cache-cell? obj))
- ((symbol? obj)
- (unless (symbol-interned? obj)
- (error "uninterned symbol cannot be saved to object file" obj))
- (let ((str-label (recur (symbol->string obj))))
- (init! (lambda (asm label)
- (emit-make-non-immediate asm 1 str-label)
- (emit-string->symbol asm 1 1)
- (emit-static-set! asm 1 label 0)
- 1))))
- ((string? obj)
- (patch! 1 (make-stringbuf obj)))
- ((keyword? obj)
- (let ((sym-label (recur (keyword->symbol obj))))
- (init! (lambda (asm label)
- (emit-static-ref asm 1 sym-label)
- (emit-symbol->keyword asm 1 1)
- (emit-static-set! asm 1 label 0)
- 1))))
- ((number? obj)
- (let ((str-label (recur (number->string obj))))
- (init! (lambda (asm label)
- (emit-make-non-immediate asm 1 str-label)
- (emit-string->number asm 1 1)
- (emit-static-set! asm 1 label 0)
- 1))))
- ((uniform-vector-backing-store? obj))
- ((simple-uniform-vector? obj)
- (let ((width (case (array-type obj)
- ((vu8 u8 s8) 1)
- ((u16 s16) 2)
- ;; Bitvectors are addressed in 32-bit units.
- ;; Although a complex number is 8 or 16 bytes wide,
- ;; it should be byteswapped in 4 or 8 byte units.
- ((u32 s32 f32 c32 b) 4)
- ((u64 s64 f64 c64) 8)
- (else
- (error "unhandled array type" obj)))))
- (patch! 2
- (make-uniform-vector-backing-store
- (uniform-array->bytevector obj)
- width))))
- ((array? obj)
- (patch! 1 (shared-array-root obj)))
- (else
- (error "don't know how to intern" obj))))
- (cond
- ((immediate-bits asm obj) #f)
- ((vhash-assoc obj (asm-constants asm)) => cdr)
- (else
- (let ((label (gensym "constant")))
- ;; Note that calling intern may mutate asm-constants and asm-inits.
- (intern! obj label)
- (set-asm-constants! asm (vhash-cons obj label (asm-constants asm)))
- label))))
- (define (intern-non-immediate asm obj)
- "Intern a non-immediate into the constant table, and return its
- label."
- (when (immediate-bits asm obj)
- (error "expected a non-immediate" obj))
- (intern-constant asm obj))
- (define (intern-cache-cell asm key)
- "Intern a cache cell into the constant table, and return its label.
- If there is already a cache cell with the given scope and key, it is
- returned instead."
- (intern-constant asm (make-cache-cell key)))
- ;;;
- ;;; Macro assemblers bridge the gap between primitive instructions and
- ;;; some higher-level operations.
- ;;;
- (eval-when (expand)
- (define-syntax define-macro-assembler
- (lambda (x)
- (syntax-case x ()
- ((_ (name arg ...) body body* ...)
- (with-syntax ((emit (id-append #'name #'emit- #'name)))
- #'(begin
- (define emit
- (let ((emit (lambda (arg ...) body body* ...)))
- (hashq-set! assemblers 'name emit)
- emit))
- (export emit))))))))
- (define-macro-assembler (load-constant asm dst obj)
- (cond
- ((scm->immediate-bits obj)
- => (lambda (bits)
- (cond
- ((and (< dst 256) (truncate-bits bits 16 #t))
- (emit-make-immediate asm dst obj))
- ((and (< dst 256) (truncate-bits bits 16 #f))
- (emit-make-short-immediate asm dst obj))
- ((truncate-bits bits 32 (eqv? (asm-word-size asm) 4))
- (emit-make-long-immediate asm dst obj))
- ((and (eqv? (asm-word-size asm) 8)
- (truncate-bits bits 64 #t))
- (emit-make-long-long-immediate asm dst obj))
- (else
- (emit-static-ref asm dst (intern-non-immediate asm obj))))))
- ((statically-allocatable? obj)
- (emit-make-non-immediate asm dst (intern-non-immediate asm obj)))
- (else
- (emit-static-ref asm dst (intern-non-immediate asm obj)))))
- (define-macro-assembler (load-static-procedure asm dst label)
- (let ((loc (intern-constant asm (make-static-procedure label))))
- (emit-make-non-immediate asm dst loc)))
- (define-syntax define-immediate-tag=?-macro-assembler
- (syntax-rules ()
- ((_ name #f mask tag) #f)
- ((_ name pred mask tag)
- (define-macro-assembler (pred asm slot)
- (emit-immediate-tag=? asm slot mask tag)))))
- (visit-immediate-tags define-immediate-tag=?-macro-assembler)
- (define-syntax-rule (define-heap-tag=?-macro-assembler name pred mask tag)
- (define-macro-assembler (pred asm slot)
- (emit-heap-tag=? asm slot mask tag)))
- (visit-heap-tags define-heap-tag=?-macro-assembler)
- (define-syntax-rule (define-scm<-scm-scm-intrinsic name)
- (define-macro-assembler (name asm dst a b)
- (emit-call-scm<-scm-scm asm dst a b (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm<-scm-uimm-intrinsic name)
- (define-macro-assembler (name asm dst a b)
- (emit-call-scm<-scm-uimm asm dst a b (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm-sz-u32-intrinsic name)
- (define-macro-assembler (name asm a b c)
- (emit-call-scm-sz-u32 asm a b c (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm<-scm-intrinsic name)
- (define-macro-assembler (name asm dst src)
- (emit-call-scm<-scm asm dst src (intrinsic-name->index 'name))))
- (define-syntax-rule (define-f64<-scm-intrinsic name)
- (define-macro-assembler (name asm dst src)
- (emit-call-f64<-scm asm dst src (intrinsic-name->index 'name))))
- (define-syntax-rule (define-f64<-f64-intrinsic name)
- (define-macro-assembler (name asm dst src)
- (emit-call-f64<-f64 asm dst src (intrinsic-name->index 'name))))
- (define-syntax-rule (define-f64<-f64-f64-intrinsic name)
- (define-macro-assembler (name asm dst a b)
- (emit-call-f64<-f64-f64 asm dst a b (intrinsic-name->index 'name))))
- (define-syntax-rule (define-u64<-scm-intrinsic name)
- (define-macro-assembler (name asm dst src)
- (emit-call-u64<-scm asm dst src (intrinsic-name->index 'name))))
- (define-syntax-rule (define-s64<-scm-intrinsic name)
- (define-macro-assembler (name asm dst src)
- (emit-call-s64<-scm asm dst src (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm<-u64-intrinsic name)
- (define-macro-assembler (name asm dst src)
- (emit-call-scm<-u64 asm dst src (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm<-s64-intrinsic name)
- (define-macro-assembler (name asm dst src)
- (emit-call-scm<-s64 asm dst src (intrinsic-name->index 'name))))
- (define-syntax-rule (define-thread-intrinsic name)
- (define-macro-assembler (name asm)
- (emit-call-thread asm (intrinsic-name->index 'name))))
- (define-syntax-rule (define-thread-scm-intrinsic name)
- (define-macro-assembler (name asm a)
- (emit-call-thread-scm asm a (intrinsic-name->index 'name))))
- (define-syntax-rule (define-thread-scm-scm-intrinsic name)
- (define-macro-assembler (name asm a b)
- (emit-call-thread-scm-scm asm a b (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm<-thread-scm-intrinsic name)
- (define-macro-assembler (name asm dst src)
- (emit-call-scm<-thread-scm asm dst src (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm<-scm-u64-intrinsic name)
- (define-macro-assembler (name asm dst a b)
- (emit-call-scm<-scm-u64 asm dst a b (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm<-scm-bool-intrinsic name)
- (define-macro-assembler (name asm dst a b)
- (emit-call-scm<-scm-uimm asm dst a (if b 1 0) (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm<-thread-intrinsic name)
- (define-macro-assembler (name asm dst)
- (emit-call-scm<-thread asm dst (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm-scm-intrinsic name)
- (define-macro-assembler (name asm a b)
- (emit-call-scm-scm asm a b (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm-uimm-scm-intrinsic name)
- (define-macro-assembler (name asm a b c)
- (emit-call-scm-uimm-scm asm a b c (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm-scm-scm-intrinsic name)
- (define-macro-assembler (name asm a b c)
- (emit-call-scm-scm-scm asm a b c (intrinsic-name->index 'name))))
- (define-syntax-rule (define-scm<-scmn-scmn-intrinsic name)
- (define-macro-assembler (name asm dst a b)
- (unless (statically-allocatable? a) (error "not statically allocatable" a))
- (unless (statically-allocatable? b) (error "not statically allocatable" b))
- (let ((a (intern-constant asm a))
- (b (intern-constant asm b)))
- (emit-call-scm<-scmn-scmn asm dst a b (intrinsic-name->index 'name)))))
- (define-scm<-scm-scm-intrinsic add)
- (define-scm<-scm-uimm-intrinsic add/immediate)
- (define-scm<-scm-scm-intrinsic sub)
- (define-scm<-scm-uimm-intrinsic sub/immediate)
- (define-scm<-scm-scm-intrinsic mul)
- (define-scm<-scm-scm-intrinsic div)
- (define-scm<-scm-scm-intrinsic quo)
- (define-scm<-scm-scm-intrinsic rem)
- (define-scm<-scm-scm-intrinsic mod)
- (define-scm<-scm-intrinsic inexact)
- (define-scm<-scm-intrinsic abs)
- (define-scm<-scm-intrinsic sqrt)
- (define-scm<-scm-intrinsic floor)
- (define-scm<-scm-intrinsic ceiling)
- (define-scm<-scm-intrinsic sin)
- (define-scm<-scm-intrinsic cos)
- (define-scm<-scm-intrinsic tan)
- (define-scm<-scm-intrinsic asin)
- (define-scm<-scm-intrinsic acos)
- (define-scm<-scm-intrinsic atan)
- (define-scm<-scm-scm-intrinsic atan2)
- (define-f64<-f64-intrinsic fabs)
- (define-f64<-f64-intrinsic fsqrt)
- (define-f64<-f64-intrinsic ffloor)
- (define-f64<-f64-intrinsic fceiling)
- (define-f64<-f64-intrinsic fsin)
- (define-f64<-f64-intrinsic fcos)
- (define-f64<-f64-intrinsic ftan)
- (define-f64<-f64-intrinsic fasin)
- (define-f64<-f64-intrinsic facos)
- (define-f64<-f64-intrinsic fatan)
- (define-f64<-f64-f64-intrinsic fatan2)
- (define-scm<-scm-scm-intrinsic logand)
- (define-scm<-scm-scm-intrinsic logior)
- (define-scm<-scm-scm-intrinsic logxor)
- (define-scm<-scm-scm-intrinsic logsub)
- (define-scm-sz-u32-intrinsic string-set!)
- (define-scm<-scm-intrinsic string->number)
- (define-scm<-scm-intrinsic string->symbol)
- (define-scm<-scm-intrinsic symbol->keyword)
- (define-scm<-scm-intrinsic class-of)
- (define-f64<-scm-intrinsic scm->f64)
- (define-u64<-scm-intrinsic scm->u64)
- (define-u64<-scm-intrinsic scm->u64/truncate)
- (define-s64<-scm-intrinsic scm->s64)
- (define-scm<-u64-intrinsic u64->scm)
- (define-scm<-s64-intrinsic s64->scm)
- (define-thread-scm-scm-intrinsic wind)
- (define-thread-intrinsic unwind)
- (define-thread-scm-scm-intrinsic push-fluid)
- (define-thread-intrinsic pop-fluid)
- (define-scm<-thread-scm-intrinsic fluid-ref)
- (define-thread-scm-scm-intrinsic fluid-set!)
- (define-thread-scm-intrinsic push-dynamic-state)
- (define-thread-intrinsic pop-dynamic-state)
- (define-scm<-scm-u64-intrinsic lsh)
- (define-scm<-scm-u64-intrinsic rsh)
- (define-scm<-scm-uimm-intrinsic lsh/immediate)
- (define-scm<-scm-uimm-intrinsic rsh/immediate)
- (define-scm<-scm-bool-intrinsic resolve-module)
- (define-scm<-scm-scm-intrinsic module-variable)
- (define-scm<-scm-scm-intrinsic lookup)
- (define-scm<-scm-scm-intrinsic lookup-bound)
- (define-scm<-scmn-scmn-intrinsic lookup-bound-public)
- (define-scm<-scmn-scmn-intrinsic lookup-bound-private)
- (define-scm<-scm-scm-intrinsic define!)
- (define-scm<-thread-intrinsic current-module)
- (define-scm<-scm-intrinsic $car)
- (define-scm<-scm-intrinsic $cdr)
- (define-scm-scm-intrinsic $set-car!)
- (define-scm-scm-intrinsic $set-cdr!)
- (define-scm<-scm-intrinsic $variable-ref)
- (define-scm-scm-intrinsic $variable-set!)
- (define-scm<-scm-intrinsic $vector-length)
- (define-scm<-scm-scm-intrinsic $vector-ref)
- (define-scm-scm-scm-intrinsic $vector-set!)
- (define-scm<-scm-uimm-intrinsic $vector-ref/immediate)
- (define-scm-uimm-scm-intrinsic $vector-set!/immediate)
- (define-scm<-scm-scm-intrinsic $allocate-struct)
- (define-scm<-scm-intrinsic $struct-vtable)
- (define-scm<-scm-scm-intrinsic $struct-ref)
- (define-scm-scm-scm-intrinsic $struct-set!)
- (define-scm<-scm-uimm-intrinsic $struct-ref/immediate)
- (define-scm-uimm-scm-intrinsic $struct-set!/immediate)
- (define-macro-assembler (begin-program asm label properties)
- (emit-label asm label)
- (let ((meta (make-meta label properties (asm-start asm))))
- (set-asm-meta! asm (cons meta (asm-meta asm))))
- (emit-instrument-entry* asm))
- (define-macro-assembler (end-program asm)
- (let ((meta (car (asm-meta asm))))
- (set-meta-high-pc! meta (asm-start asm))
- (set-meta-arities! meta (reverse (meta-arities meta)))
- (set-asm-constants!
- asm
- (vhash-cons (make-jit-data (meta-low-pc meta) (meta-high-pc meta))
- (meta-jit-data-label meta)
- (asm-constants asm)))))
- (define-macro-assembler (begin-standard-arity asm has-closure? req nlocals
- alternate)
- (emit-begin-opt-arity asm has-closure? req '() #f nlocals alternate))
- (define-macro-assembler (begin-opt-arity asm has-closure? req opt rest nlocals
- alternate)
- (emit-begin-kw-arity asm has-closure? req opt rest '() #f nlocals alternate))
- (define-macro-assembler (begin-kw-arity asm has-closure? req opt rest kw-indices
- allow-other-keys? nlocals alternate)
- (assert-match req ((? symbol?) ...) "list of symbols")
- (assert-match opt ((? symbol?) ...) "list of symbols")
- (assert-match rest (or #f (? symbol?)) "#f or symbol")
- (assert-match kw-indices (((? keyword?) . (? integer?)) ...)
- "alist of keyword -> integer")
- (assert-match allow-other-keys? (? boolean?) "boolean")
- (assert-match nlocals (? integer?) "integer")
- (assert-match alternate (or #f (? exact-integer?) (? symbol?)) "#f or symbol")
- (let* ((meta (car (asm-meta asm)))
- (arity (make-arity req opt rest kw-indices allow-other-keys?
- has-closure?
- ;; Include the initial instrument-entry in
- ;; the first arity.
- (if (null? (meta-arities meta))
- (meta-low-pc meta)
- (asm-start asm))
- #f '()))
- ;; The procedure itself is in slot 0, in the standard calling
- ;; convention. For procedure prologues, nreq includes the
- ;; procedure, so here we add 1.
- (nclosure (if has-closure? 1 0))
- (nreq (+ nclosure (length req)))
- (nopt (length opt))
- (rest? (->bool rest)))
- (set-meta-arities! meta (cons arity (meta-arities meta)))
- (cond
- ((or allow-other-keys? (pair? kw-indices))
- (emit-kw-prelude asm nreq nopt rest? kw-indices allow-other-keys?
- nlocals alternate))
- ((or rest? (pair? opt))
- (emit-opt-prelude asm nreq nopt rest? nlocals alternate))
- (else
- (emit-standard-prelude asm nreq nlocals alternate)))))
- (define-macro-assembler (begin-unchecked-arity asm has-closure? req nlocals)
- (assert-match req ((? symbol?) ...) "list of symbols")
- (assert-match nlocals (? integer?) "integer")
- (let* ((meta (car (asm-meta asm)))
- (arity (make-arity req '() #f '() #f has-closure?
- (meta-low-pc meta) #f '()))
- (nclosure (if has-closure? 1 0))
- (nreq (+ nclosure (length req))))
- (set-meta-arities! meta (cons arity (meta-arities meta)))
- (emit-unchecked-prelude asm nreq nlocals)))
- (define-macro-assembler (end-arity asm)
- (let ((arity (car (meta-arities (car (asm-meta asm))))))
- (set-arity-definitions! arity (reverse (arity-definitions arity)))
- (set-arity-high-pc! arity (asm-start asm))))
- (define-macro-assembler (unchecked-prelude asm nreq nlocals)
- (unless (= nlocals nreq)
- (emit-alloc-frame asm nlocals)))
- (define-macro-assembler (standard-prelude asm nreq nlocals alternate)
- (cond
- (alternate
- (emit-arguments<=? asm nreq)
- (emit-jne asm alternate)
- (emit-alloc-frame asm nlocals))
- ((and (< nreq (ash 1 12)) (< (- nlocals nreq) (ash 1 12)))
- (emit-assert-nargs-ee/locals asm nreq (- nlocals nreq)))
- (else
- (emit-assert-nargs-ee asm nreq)
- (emit-alloc-frame asm nlocals))))
- (define-macro-assembler (opt-prelude asm nreq nopt rest? nlocals alternate)
- (if alternate
- (begin
- (emit-arguments<=? asm nreq)
- (emit-jl asm alternate))
- (emit-assert-nargs-ge asm nreq))
- (cond
- (rest?
- (unless (zero? nopt)
- (emit-bind-optionals asm (+ nreq nopt)))
- (emit-bind-rest asm (+ nreq nopt)))
- (alternate
- (emit-arguments<=? asm (+ nreq nopt))
- ;; The arguments<=? instruction sets NONE to indicate greater-than,
- ;; whereas for <, NONE usually indicates greater-than-or-equal,
- ;; hence the name jge. Perhaps we just need to rename jge to
- ;; br-if-none.
- (emit-jge asm alternate)
- (unless (zero? nopt)
- (emit-bind-optionals asm (+ nreq nopt))))
- (else
- (emit-assert-nargs-le asm (+ nreq nopt))
- (unless (zero? nopt)
- (emit-bind-optionals asm (+ nreq nopt)))))
- (emit-alloc-frame asm nlocals))
- (define-macro-assembler (kw-prelude asm nreq nopt rest? kw-indices
- allow-other-keys? nlocals alternate)
- (if alternate
- (begin
- (emit-arguments<=? asm nreq)
- (emit-jl asm alternate)
- (unless rest?
- (emit-positional-arguments<=? asm nreq (+ nreq nopt))
- (emit-jge asm alternate)))
- (emit-assert-nargs-ge asm nreq))
- (let ((ntotal (fold (lambda (kw ntotal)
- (match kw
- (((? keyword?) . idx)
- (max (1+ idx) ntotal))))
- (+ nreq nopt) kw-indices)))
- ;; FIXME: port 581f410f
- (emit-bind-kwargs asm nreq
- (pack-flags allow-other-keys? rest?)
- (+ nreq nopt)
- ntotal
- (intern-constant asm kw-indices))
- (emit-alloc-frame asm nlocals)))
- (define-macro-assembler (label asm sym)
- (hashq-set! (asm-labels asm) sym (asm-start asm)))
- (define-macro-assembler (source asm source)
- (set-asm-sources! asm (acons (asm-start asm) source (asm-sources asm))))
- (define-macro-assembler (definition asm name slot representation)
- (let* ((arity (car (meta-arities (car (asm-meta asm)))))
- (def (vector name slot representation
- (- (asm-start asm) (arity-low-pc arity)))))
- (set-arity-definitions! arity (cons def (arity-definitions arity)))))
- (define-macro-assembler (cache-ref asm dst key)
- (emit-static-ref asm dst (intern-cache-cell asm key)))
- (define-macro-assembler (cache-set! asm key val)
- (emit-static-set! asm val (intern-cache-cell asm key) 0))
- (define-macro-assembler (slot-map asm proc-slot slot-map)
- (unless (zero? slot-map)
- (set-asm-slot-maps! asm (cons
- (cons* (asm-start asm) proc-slot slot-map)
- (asm-slot-maps asm)))))
- ;;;
- ;;; Helper for linking objects.
- ;;;
- (define (make-object asm name size writer relocs labels . kwargs)
- "Make a linker object. This helper handles interning the name in the
- shstrtab, assigning the size, allocating a fresh index, and defining a
- corresponding linker symbol for the start of the section."
- (let ((name-idx (intern-section-name! asm (symbol->string name)))
- (index (asm-next-section-number asm)))
- (set-asm-next-section-number! asm (1+ index))
- (make-linker-object (symbol->string name)
- (apply make-elf-section
- #:index index
- #:name name-idx
- #:size size
- kwargs)
- size writer relocs
- (cons (make-linker-symbol name 0) labels))))
- ;;;
- ;;; Linking the constant table. This code is somewhat intertwingled
- ;;; with the intern-constant code above, as that procedure also
- ;;; residualizes instructions to initialize constants at load time.
- ;;;
- (define (write-immediate asm buf pos bits)
- (let ((endianness (asm-endianness asm)))
- (case (asm-word-size asm)
- ((4) (bytevector-u32-set! buf pos bits endianness))
- ((8) (bytevector-u64-set! buf pos bits endianness))
- (else (error "bad word size" asm)))))
- (define (write-placeholder asm buf pos)
- (write-immediate asm buf pos (immediate-bits asm #f)))
- (define (emit-init-constants asm)
- "If there is writable data that needs initialization at runtime, emit
- a procedure to do that and return its label. Otherwise return
- @code{#f}."
- (let* ((inits (asm-inits asm)))
- (and (not (vlist-null? inits))
- (let ((label (gensym "init-constants")))
- (emit-begin-program asm label '())
- (emit-assert-nargs-ee/locals asm 1 1)
- (let lp ((n (1- (vlist-length inits))))
- (match (vlist-ref inits n)
- ((label . #(#f #t ((dst . field))))
- ;; Special case in which emit-static-patch is actually
- ;; an optimization.
- (emit-static-patch! asm dst field label))
- ((label . #(emit-init static? patches))
- (let ((slot-from-init (and emit-init (emit-init asm label))))
- (unless (null? patches)
- (let ((slot (or slot-from-init
- (begin
- (if static?
- (emit-make-non-immediate asm 1 label)
- (emit-static-ref asm 1 label))
- 1))))
- (for-each (match-lambda
- ((dst . offset)
- (emit-static-set! asm slot dst offset)))
- patches))))))
- (unless (zero? n)
- (lp (1- n))))
- (emit-reset-frame asm 1)
- (emit-load-constant asm 0 *unspecified*)
- (emit-return-values asm)
- (emit-end-program asm)
- label))))
- (define (link-data asm data name)
- "Link the static data for a program into the @var{name} section (which
- should be .data or .rodata), and return the resulting linker object.
- @var{data} should be a vhash mapping objects to labels."
- (define (align address alignment)
- (+ address
- (modulo (- alignment (modulo address alignment)) alignment)))
- (define tc7-vector #x0d)
- (define vector-immutable-flag #x80)
- (define tc7-string #x15)
- (define string-read-only-flag #x200)
- (define tc7-stringbuf #x27)
- (define stringbuf-wide-flag #x400)
- (define tc7-syntax #x3d)
- (define syntax-has-source-flag #x100)
- (define tc7-program #x45)
- (define tc7-bytevector #x4d)
- ;; This flag is intended to be left-shifted by 7 bits.
- (define bytevector-immutable-flag #x200)
- (define tc7-array #x5d)
- (define tc7-bitvector #x5f)
- (define bitvector-immutable-flag #x80)
- (let ((word-size (asm-word-size asm))
- (endianness (asm-endianness asm)))
- (define (byte-length x)
- (cond
- ((stringbuf? x)
- (let ((x (stringbuf-string x)))
- (+ (* 2 word-size)
- (case (string-bytes-per-char x)
- ((1) (1+ (string-length x)))
- ((4) (* (1+ (string-length x)) 4))
- (else (error "bad string bytes per char" x))))))
- ((static-procedure? x)
- (* 2 word-size))
- ((string? x)
- (* 4 word-size))
- ((pair? x)
- (* 2 word-size))
- ((simple-vector? x)
- (* (1+ (vector-length x)) word-size))
- ((syntax? x)
- (* 5 word-size))
- ((jit-data? x)
- (case word-size
- ((4) (+ word-size (* 4 3)))
- ((8) (+ word-size (* 4 4))) ;; One additional uint32_t for padding.
- (else (error word-size))))
- ((simple-uniform-vector? x)
- (* 4 word-size))
- ((uniform-vector-backing-store? x)
- (bytevector-length (uniform-vector-backing-store-bytes x)))
- ((array? x)
- (* word-size (+ 3 (* 3 (array-rank x)))))
- (else
- word-size)))
- (define (write-constant-reference buf pos x)
- (let ((bits (immediate-bits asm x)))
- (if bits
- (write-immediate asm buf pos bits)
- ;; The asm-inits will fix up any reference to a
- ;; non-immediate.
- (write-placeholder asm buf pos))))
- (define (write buf pos obj)
- (cond
- ((stringbuf? obj)
- (let* ((x (stringbuf-string obj))
- (len (string-length x))
- (tag (logior tc7-stringbuf
- (if (= (string-bytes-per-char x) 1)
- 0
- stringbuf-wide-flag))))
- (case word-size
- ((4)
- (bytevector-u32-set! buf pos tag endianness)
- (bytevector-u32-set! buf (+ pos 4) len endianness))
- ((8)
- (bytevector-u64-set! buf pos tag endianness)
- (bytevector-u64-set! buf (+ pos 8) len endianness))
- (else
- (error "bad word size" asm)))
- (let ((pos (+ pos (* word-size 2))))
- (case (string-bytes-per-char x)
- ((1)
- (let lp ((i 0))
- (if (< i len)
- (let ((u8 (char->integer (string-ref x i))))
- (bytevector-u8-set! buf (+ pos i) u8)
- (lp (1+ i)))
- (bytevector-u8-set! buf (+ pos i) 0))))
- ((4)
- (let lp ((i 0))
- (if (< i len)
- (let ((u32 (char->integer (string-ref x i))))
- (bytevector-u32-set! buf (+ pos (* i 4)) u32 endianness)
- (lp (1+ i)))
- (bytevector-u32-set! buf (+ pos (* i 4)) 0 endianness))))
- (else (error "bad string bytes per char" x))))))
- ((static-procedure? obj)
- (case word-size
- ((4)
- (bytevector-u32-set! buf pos tc7-program endianness)
- (bytevector-u32-set! buf (+ pos 4) 0 endianness))
- ((8)
- (bytevector-u64-set! buf pos tc7-program endianness)
- (bytevector-u64-set! buf (+ pos 8) 0 endianness))
- (else (error "bad word size"))))
- ((cache-cell? obj)
- (write-placeholder asm buf pos))
- ((jit-data? obj)
- ;; Default initialization of 0.
- (values))
- ((string? obj)
- (let ((tag (logior tc7-string string-read-only-flag)))
- (case word-size
- ((4)
- (bytevector-u32-set! buf pos tag endianness)
- (write-placeholder asm buf (+ pos 4)) ; stringbuf
- (bytevector-u32-set! buf (+ pos 8) 0 endianness)
- (bytevector-u32-set! buf (+ pos 12) (string-length obj) endianness))
- ((8)
- (bytevector-u64-set! buf pos tag endianness)
- (write-placeholder asm buf (+ pos 8)) ; stringbuf
- (bytevector-u64-set! buf (+ pos 16) 0 endianness)
- (bytevector-u64-set! buf (+ pos 24) (string-length obj) endianness))
- (else (error "bad word size")))))
- ((pair? obj)
- (write-constant-reference buf pos (car obj))
- (write-constant-reference buf (+ pos word-size) (cdr obj)))
- ((simple-vector? obj)
- (let* ((len (vector-length obj))
- (tag (logior tc7-vector vector-immutable-flag (ash len 8))))
- (case word-size
- ((4) (bytevector-u32-set! buf pos tag endianness))
- ((8) (bytevector-u64-set! buf pos tag endianness))
- (else (error "bad word size")))
- (let lp ((i 0))
- (when (< i (vector-length obj))
- (let ((pos (+ pos word-size (* i word-size)))
- (elt (vector-ref obj i)))
- (write-constant-reference buf pos elt)
- (lp (1+ i)))))))
- ((symbol? obj)
- (write-placeholder asm buf pos))
- ((keyword? obj)
- (write-placeholder asm buf pos))
- ((syntax? obj)
- (let ((tag (logior tc7-syntax syntax-has-source-flag)))
- (case word-size
- ((4) (bytevector-u32-set! buf pos tag endianness))
- ((8) (bytevector-u64-set! buf pos tag endianness))
- (else (error "bad word size"))))
- (write-constant-reference buf (+ pos (* 1 word-size))
- (syntax-expression obj))
- (write-constant-reference buf (+ pos (* 2 word-size))
- (syntax-wrap obj))
- (write-constant-reference buf (+ pos (* 3 word-size))
- (syntax-module obj))
- (write-constant-reference buf (+ pos (* 4 word-size))
- (syntax-sourcev obj)))
- ((number? obj)
- (write-placeholder asm buf pos))
- ((simple-uniform-vector? obj)
- (let ((tag (if (bitvector? obj)
- (logior tc7-bitvector
- bitvector-immutable-flag)
- (logior tc7-bytevector
- ;; Bytevector immutable flag also shifted
- ;; left.
- (ash (logior bytevector-immutable-flag
- (array-type-code obj))
- 7)))))
- (case word-size
- ((4)
- (bytevector-u32-set! buf pos tag endianness)
- (bytevector-u32-set! buf (+ pos 4)
- (if (bitvector? obj)
- (bitvector-length obj)
- (bytevector-length obj))
- endianness) ; length
- (bytevector-u32-set! buf (+ pos 8) 0 endianness) ; pointer
- (write-placeholder asm buf (+ pos 12))) ; owner
- ((8)
- (bytevector-u64-set! buf pos tag endianness)
- (bytevector-u64-set! buf (+ pos 8)
- (if (bitvector? obj)
- (bitvector-length obj)
- (bytevector-length obj))
- endianness) ; length
- (bytevector-u64-set! buf (+ pos 16) 0 endianness) ; pointer
- (write-placeholder asm buf (+ pos 24))) ; owner
- (else (error "bad word size")))))
- ((uniform-vector-backing-store? obj)
- (let ((bv (uniform-vector-backing-store-bytes obj)))
- (bytevector-copy! bv 0 buf pos (bytevector-length bv))
- (unless (eq? endianness (native-endianness))
- (case (uniform-vector-backing-store-element-size obj)
- ((1) #f) ;; Nothing to do.
- ((2) (byte-swap/2! buf pos (+ pos (bytevector-length bv))))
- ((4) (byte-swap/4! buf pos (+ pos (bytevector-length bv))))
- ((8) (byte-swap/8! buf pos (+ pos (bytevector-length bv))))
- (else (error "FIXME: Implement byte order swap"))))))
- ((array? obj)
- (let-values
- ;; array tag + rank
- ;; see libguile/arrays.h: SCM_I_ARRAY_NDIM, SCM_I_ARRAYP, scm_i_raw_array
- (((tag) (logior tc7-array (ash (array-rank obj) 17)))
- ((bv-set! bvs-set!)
- (case word-size
- ((4) (values bytevector-u32-set! bytevector-s32-set!))
- ((8) (values bytevector-u64-set! bytevector-s64-set!))
- (else (error "bad word size")))))
- (bv-set! buf pos tag endianness)
- (write-placeholder asm buf (+ pos word-size)) ; root vector (fixed later)
- (bv-set! buf (+ pos (* word-size 2)) 0 endianness) ; base
- (let lp ((pos (+ pos (* word-size 3)))
- (bounds (array-shape obj))
- (incs (shared-array-increments obj)))
- (when (pair? bounds)
- (bvs-set! buf pos (first (first bounds)) endianness)
- (bvs-set! buf (+ pos word-size) (second (first bounds)) endianness)
- (bvs-set! buf (+ pos (* word-size 2)) (first incs) endianness)
- (lp (+ pos (* 3 word-size)) (cdr bounds) (cdr incs))))))
- (else
- (error "unrecognized object" obj))))
- (define (add-relocs obj pos relocs)
- (match obj
- (($ <jit-data> low-pc high-pc)
- ;; Patch "start" and "end" fields of "struct jit_data".
- (cons* (make-linker-reloc 'rel32/1 (+ pos word-size 4)
- (+ low-pc word-size 4)
- '.rtl-text)
- (make-linker-reloc 'rel32/1 (+ pos word-size 8)
- (+ high-pc word-size 8)
- '.rtl-text)
- relocs))
- (_ relocs)))
- (cond
- ((vlist-null? data) #f)
- (else
- (let* ((byte-len (vhash-fold (lambda (k v len)
- (+ (byte-length k) (align len 8)))
- 0 data)))
- (let lp ((i 0) (pos 0) (relocs '()) (symbols '()))
- (if (< i (vlist-length data))
- (match (vlist-ref data i)
- ((obj . obj-label)
- (lp (1+ i)
- (align (+ (byte-length obj) pos) 8)
- (add-relocs obj pos relocs)
- (cons (make-linker-symbol obj-label pos) symbols))))
- (make-object asm name byte-len
- (lambda (bv)
- (let loop ((i 0) (pos 0))
- (when (< i (vlist-length data))
- (match (vlist-ref data i)
- ((obj . obj-label)
- (write bv pos obj)
- (loop (1+ i)
- (align
- (+ (byte-length obj) pos)
- 8)))))))
- relocs symbols
- #:flags (match name
- ('.data (logior SHF_ALLOC SHF_WRITE))
- ('.rodata SHF_ALLOC))))))))))
- (define (link-constants asm)
- "Link sections to hold constants needed by the program text emitted
- using @var{asm}.
- Returns three values: an object for the .rodata section, an object for
- the .data section, and a label for an initialization procedure. Any of
- these may be @code{#f}."
- (define (shareable? x)
- (cond
- ((stringbuf? x) #t)
- ((pair? x)
- (and (immediate-bits asm (car x)) (immediate-bits asm (cdr x))))
- ((simple-vector? x)
- (let lp ((i 0))
- (or (= i (vector-length x))
- (and (immediate-bits asm (vector-ref x i))
- (lp (1+ i))))))
- ((uniform-vector-backing-store? x) #t)
- (else #f)))
- (let* ((init-constants (emit-init-constants asm))
- (constants (asm-constants asm))
- (len (vlist-length constants)))
- (let lp ((i 0)
- (ro vlist-null)
- (rw vlist-null))
- (if (= i len)
- (values (link-data asm ro '.rodata)
- (link-data asm rw '.data)
- init-constants)
- (match (vlist-ref constants i)
- ((obj . label)
- (if (shareable? obj)
- (lp (1+ i) (vhash-consq obj label ro) rw)
- (lp (1+ i) ro (vhash-consq obj label rw)))))))))
- ;;;
- ;;; Linking program text.
- ;;;
- (define (process-relocs relocs labels)
- "Return a list of linker relocations for references to symbols defined
- outside the text section."
- (fold (lambda (reloc tail)
- (match reloc
- ((type label base offset)
- (let ((abs (hashq-ref labels label))
- (dst (+ base offset)))
- (case type
- ((s32)
- (if abs
- tail
- (cons (make-linker-reloc 'rel32/4 dst offset label)
- tail)))
- ((x8-s24)
- (unless abs
- (error "unbound near relocation" reloc))
- tail)
- (else (error "bad relocation kind" reloc)))))))
- '()
- relocs))
- (define (patch-relocs! buf relocs labels)
- "Patch up internal x8-s24 relocations, and any s32 relocations that
- reference symbols in the text section."
- (for-each (lambda (reloc)
- (match reloc
- ((type label base offset)
- (let ((abs (hashq-ref labels label))
- (dst (+ base offset)))
- (case type
- ((s32)
- (when abs
- (let ((rel (- abs base)))
- (unless (zero? (logand rel #x3))
- (error "reloc not in 32-bit units!"))
- (bytevector-s32-native-set! buf dst (ash rel -2)))))
- ((x8-s24)
- (unless abs
- (error "unbound near relocation" reloc))
- (let ((rel (- abs base))
- (u32 (bytevector-u32-native-ref buf dst)))
- (unless (zero? (logand rel #x3))
- (error "reloc not in 32-bit units!"))
- (bytevector-u32-native-set! buf dst
- (pack-u8-s24 (logand u32 #xff)
- (ash rel -2)))))
- (else (error "bad relocation kind" reloc)))))))
- relocs))
- (define (process-labels labels)
- "Define linker symbols for the label-offset map in @var{labels}.
- The offsets are expected to be expressed in words."
- (hash-map->list (lambda (label loc)
- (make-linker-symbol label loc))
- labels))
- (define (link-text-object asm)
- "Link the .rtl-text section, swapping the endianness of the bytes if
- needed."
- (let ((size (asm-pos asm)))
- (make-object asm '.rtl-text size
- (lambda (buf)
- (bytevector-copy! (asm-buf asm) 0 buf 0 size)
- (unless (eq? (asm-endianness asm) (native-endianness))
- (byte-swap/4! buf))
- (patch-relocs! buf (asm-relocs asm) (asm-labels asm)))
- (process-relocs (asm-relocs asm)
- (asm-labels asm))
- (process-labels (asm-labels asm)))))
- ;;;
- ;;; Create the frame maps. These maps are used by GC to identify dead
- ;;; slots in pending call frames, to avoid marking them. We only do
- ;;; this when frame makes a non-tail call, as that is the common case.
- ;;; Only the topmost frame will see a GC at any other point, but we mark
- ;;; top frames conservatively as serializing live slot maps at every
- ;;; instruction would take up too much space in the object file.
- ;;;
- ;; The .guile.frame-maps section starts with two packed u32 values: one
- ;; indicating the offset of the first byte of the .rtl-text section, and
- ;; another indicating the relative offset in bytes of the slots data.
- (define frame-maps-prefix-len 8)
- ;; Each header is 8 bytes: 4 for the offset from .rtl_text, and 4 for
- ;; the offset of the slot map from the beginning of the
- ;; .guile.frame-maps section. The length of a frame map depends on the
- ;; frame size at the call site, and is not encoded into this section as
- ;; it is available at run-time.
- (define frame-map-header-len 8)
- (define (link-frame-maps asm)
- (define (map-byte-length proc-slot)
- (ceiling-quotient (* 2 (- proc-slot 2)) 8))
- (define (make-frame-maps maps count map-len)
- (let* ((endianness (asm-endianness asm))
- (header-pos frame-maps-prefix-len)
- (map-pos (+ header-pos (* count frame-map-header-len)))
- (size (+ map-pos map-len)))
- (define (write! bv)
- (bytevector-u32-set! bv 4 map-pos endianness)
- (let lp ((maps maps) (header-pos header-pos) (map-pos map-pos))
- (match maps
- (()
- #t)
- (((pos proc-slot . map) . maps)
- (bytevector-u32-set! bv header-pos pos endianness)
- (bytevector-u32-set! bv (+ header-pos 4) map-pos endianness)
- (let write-bytes ((map-pos map-pos)
- (map map)
- (byte-length (map-byte-length proc-slot)))
- (if (zero? byte-length)
- (lp maps (+ header-pos frame-map-header-len) map-pos)
- (begin
- (bytevector-u8-set! bv map-pos (logand map #xff))
- (write-bytes (1+ map-pos) (ash map -8)
- (1- byte-length)))))))))
- (make-object asm '.guile.frame-maps size write!
- (list (make-linker-reloc 'abs32/1 0 0 '.rtl-text))
- '() #:type SHT_PROGBITS #:flags SHF_ALLOC)))
- (match (asm-slot-maps asm)
- (() #f)
- (in
- (let lp ((in in) (out '()) (count 0) (map-len 0))
- (match in
- (() (make-frame-maps out count map-len))
- (((and head (pos proc-slot . map)) . in)
- (lp in (cons head out)
- (1+ count)
- (+ (map-byte-length proc-slot) map-len))))))))
- ;;;
- ;;; Linking other sections of the ELF file, like the dynamic segment,
- ;;; the symbol table, etc.
- ;;;
- ;; FIXME: Define these somewhere central, shared with C.
- (define *bytecode-major-version* #x0300)
- (define *bytecode-minor-version* 6)
- (define (link-dynamic-section asm text rw rw-init frame-maps)
- "Link the dynamic section for an ELF image with bytecode @var{text},
- given the writable data section @var{rw} needing fixup from the
- procedure with label @var{rw-init}. @var{rw-init} may be false. If
- @var{rw} is true, it will be added to the GC roots at runtime."
- (define-syntax-rule (emit-dynamic-section word-size %set-uword! reloc-type)
- (let* ((endianness (asm-endianness asm))
- (words 6)
- (words (if rw (+ words 4) words))
- (words (if rw-init (+ words 2) words))
- (words (if frame-maps (+ words 2) words))
- (size (* word-size words)))
- (define relocs
- ;; This must match the 'set-label!' calls below.
- (let ((reloc (lambda (i label)
- (make-linker-reloc 'reloc-type
- (* i word-size) 0 label))))
- `(,(reloc 3 '.rtl-text)
- ,@(if rw
- (list (reloc 5 '.data))
- '())
- ,@(if (and rw rw-init)
- (list (reloc 9 rw-init))
- '())
- ,@(if frame-maps
- (list (reloc (- words 3) '.guile.frame-maps))
- '()))))
- (define (write! bv)
- (define (set-uword! i uword)
- (%set-uword! bv (* i word-size) uword endianness))
- (define (set-label! i label)
- (%set-uword! bv (* i word-size) 0 endianness))
- (set-uword! 0 DT_GUILE_VM_VERSION)
- (set-uword! 1 (logior (ash *bytecode-major-version* 16)
- *bytecode-minor-version*))
- (set-uword! 2 DT_GUILE_ENTRY)
- (set-label! 3 '.rtl-text)
- (when rw
- ;; Add roots to GC.
- (set-uword! 4 DT_GUILE_GC_ROOT)
- (set-label! 5 '.data)
- (set-uword! 6 DT_GUILE_GC_ROOT_SZ)
- (set-uword! 7 (linker-object-size rw))
- (when rw-init
- (set-uword! 8 DT_INIT) ; constants
- (set-label! 9 rw-init)))
- (when frame-maps
- (set-uword! (- words 4) DT_GUILE_FRAME_MAPS)
- (set-label! (- words 3) '.guile.frame-maps))
- (set-uword! (- words 2) DT_NULL)
- (set-uword! (- words 1) 0))
- (make-object asm '.dynamic size write!
- relocs '()
- #:type SHT_DYNAMIC #:flags SHF_ALLOC)))
- (case (asm-word-size asm)
- ((4) (emit-dynamic-section 4 bytevector-u32-set! abs32/1))
- ((8) (emit-dynamic-section 8 bytevector-u64-set! abs64/1))
- (else (error "bad word size" asm))))
- (define (link-shstrtab asm)
- "Link the string table for the section headers."
- (intern-section-name! asm ".shstrtab")
- (make-object asm '.shstrtab
- (string-table-size (asm-shstrtab asm))
- (string-table-writer (asm-shstrtab asm))
- '() '()
- #:type SHT_STRTAB #:flags 0))
- (define (link-symtab text-section asm)
- (let* ((endianness (asm-endianness asm))
- (word-size (asm-word-size asm))
- (size (elf-symbol-len word-size))
- (meta (reverse (asm-meta asm)))
- (n (length meta))
- (strtab (make-string-table)))
- (define (intern-string! name)
- (string-table-intern! strtab (if name (symbol->string name) "")))
- (define names
- (map (lambda (meta n)
- (intern-string! (meta-name meta)))
- meta (iota n)))
- (define (write-symbols! bv)
- (for-each (lambda (name meta n)
- (write-elf-symbol bv (* n size)
- endianness word-size
- (make-elf-symbol
- #:name name
- ;; Symbol value and size are measured in
- ;; bytes, not u32s.
- #:value (meta-low-pc meta)
- #:size (- (meta-high-pc meta)
- (meta-low-pc meta))
- #:type STT_FUNC
- #:visibility STV_HIDDEN
- #:shndx (elf-section-index
- text-section))))
- names meta (iota n)))
- (let ((strtab (make-object asm '.strtab
- (string-table-size strtab)
- (string-table-writer strtab)
- '() '()
- #:type SHT_STRTAB #:flags 0)))
- (values (make-object asm '.symtab
- (* n size) write-symbols!
- '() '()
- #:type SHT_SYMTAB #:flags 0 #:entsize size
- #:link (elf-section-index
- (linker-object-section strtab)))
- strtab))))
- ;;; The .guile.arities section describes the arities that a function can
- ;;; have. It is in two parts: a sorted array of headers describing
- ;;; basic arities, and an array of links out to a string table (and in
- ;;; the case of keyword arguments, to the data section) for argument
- ;;; names. The whole thing is prefixed by a uint32 indicating the
- ;;; offset of the end of the headers array.
- ;;;
- ;;; The arity headers array is a packed array of structures of the form:
- ;;;
- ;;; struct arity_header {
- ;;; uint32_t low_pc;
- ;;; uint32_t high_pc;
- ;;; uint32_t offset;
- ;;; uint32_t flags;
- ;;; uint32_t nreq;
- ;;; uint32_t nopt;
- ;;; uint32_t nlocals;
- ;;; }
- ;;;
- ;;; All of the offsets and addresses are 32 bits. We can expand in the
- ;;; future to use 64-bit offsets if appropriate, but there are other
- ;;; aspects of bytecode that constrain us to a total image that fits in
- ;;; 32 bits, so for the moment we'll simplify the problem space.
- ;;;
- ;;; The following flags values are defined:
- ;;;
- ;;; #x1: has-rest?
- ;;; #x2: allow-other-keys?
- ;;; #x4: has-keyword-args?
- ;;; #x8: is-case-lambda?
- ;;; #x10: is-in-case-lambda?
- ;;; #x20: elided-closure?
- ;;;
- ;;; Functions with a single arity specify their number of required and
- ;;; optional arguments in nreq and nopt, and do not have the
- ;;; is-case-lambda? flag set. Their "offset" member links to an array
- ;;; of pointers into the associated .guile.arities.strtab string table,
- ;;; identifying the argument names. This offset is relative to the
- ;;; start of the .guile.arities section.
- ;;;
- ;;; If the arity has keyword arguments -- if has-keyword-args? is set in
- ;;; the flags -- the first uint32 pointed to by offset encodes a link to
- ;;; the "keyword indices" literal, in the data section. Then follow the
- ;;; names for all locals, in order, as uleb128 values. The required
- ;;; arguments will be the first locals, followed by the optionals,
- ;;; followed by the rest argument if if has-rest? is set. The names
- ;;; point into the associated string table section.
- ;;;
- ;;; Functions with no arities have no arities information present in the
- ;;; .guile.arities section.
- ;;;
- ;;; Functions with multiple arities are preceded by a header with
- ;;; is-case-lambda? set. All other fields are 0, except low-pc and
- ;;; high-pc which should be the bounds of the whole function. Headers
- ;;; for the individual arities follow, with the is-in-case-lambda? flag
- ;;; set. In this way the whole headers array is sorted in increasing
- ;;; low-pc order, and case-lambda clauses are contained within the
- ;;; [low-pc, high-pc] of the case-lambda header.
- ;;;
- ;;; Normally the 0th argument is the closure for the function being
- ;;; called. However if the function is "well-known" -- all of its call
- ;;; sites are visible -- then the compiler may elide the closure, and
- ;;; the 0th argument is the first user-visible argument.
- ;; Length of the prefix to the arities section, in bytes.
- (define arities-prefix-len 4)
- ;; Length of an arity header, in bytes.
- (define arity-header-len (* 7 4))
- ;; Some helpers.
- (define (put-uleb128 port val)
- (let lp ((val val))
- (let ((next (ash val -7)))
- (if (zero? next)
- (put-u8 port val)
- (begin
- (put-u8 port (logior #x80 (logand val #x7f)))
- (lp next))))))
- (define (put-sleb128 port val)
- (let lp ((val val))
- (if (<= 0 (+ val 64) 127)
- (put-u8 port (logand val #x7f))
- (begin
- (put-u8 port (logior #x80 (logand val #x7f)))
- (lp (ash val -7))))))
- (define (port-position port)
- (seek port 0 SEEK_CUR))
- (define-inline (pack-arity-flags has-rest? allow-other-keys?
- has-keyword-args? is-case-lambda?
- is-in-case-lambda? elided-closure?)
- (logior (if has-rest? (ash 1 0) 0)
- (if allow-other-keys? (ash 1 1) 0)
- (if has-keyword-args? (ash 1 2) 0)
- (if is-case-lambda? (ash 1 3) 0)
- (if is-in-case-lambda? (ash 1 4) 0)
- (if elided-closure? (ash 1 5) 0)))
- (define (write-arities asm metas headers names-port strtab)
- (define (write-header pos low-pc high-pc offset flags nreq nopt nlocals)
- (unless (<= (+ nreq nopt) nlocals)
- (error "forgot to emit definition instructions?"))
- (bytevector-u32-set! headers pos low-pc (asm-endianness asm))
- (bytevector-u32-set! headers (+ pos 4) high-pc (asm-endianness asm))
- (bytevector-u32-set! headers (+ pos 8) offset (asm-endianness asm))
- (bytevector-u32-set! headers (+ pos 12) flags (asm-endianness asm))
- (bytevector-u32-set! headers (+ pos 16) nreq (asm-endianness asm))
- (bytevector-u32-set! headers (+ pos 20) nopt (asm-endianness asm))
- (bytevector-u32-set! headers (+ pos 24) nlocals (asm-endianness asm)))
- (define (write-kw-indices kw-indices relocs)
- ;; FIXME: Assert that kw-indices is already interned.
- (if (pair? kw-indices)
- (let ((pos (+ (bytevector-length headers)
- (port-position names-port)))
- (label (intern-constant asm kw-indices)))
- (put-bytevector names-port #vu8(0 0 0 0))
- (cons (make-linker-reloc 'abs32/1 pos 0 label) relocs))
- relocs))
- (define (write-arity pos arity in-case-lambda? relocs)
- (write-header pos (arity-low-pc arity)
- (arity-high-pc arity)
- ;; FIXME: Seems silly to add on bytevector-length of
- ;; headers, given the arities-prefix.
- (+ (bytevector-length headers) (port-position names-port))
- (pack-arity-flags (arity-rest arity)
- (arity-allow-other-keys? arity)
- (pair? (arity-kw-indices arity))
- #f
- in-case-lambda?
- (not (arity-has-closure? arity)))
- (length (arity-req arity))
- (length (arity-opt arity))
- (length (arity-definitions arity)))
- (let ((relocs (write-kw-indices (arity-kw-indices arity) relocs)))
- ;; Write local names.
- (let lp ((definitions (arity-definitions arity)))
- (match definitions
- (() relocs)
- ((#(name slot representation def) . definitions)
- (let ((sym (if (symbol? name)
- (string-table-intern! strtab (symbol->string name))
- 0)))
- (put-uleb128 names-port sym)
- (lp definitions)))))
- ;; Now write their definitions.
- (let lp ((definitions (arity-definitions arity)))
- (match definitions
- (() relocs)
- ((#(name slot representation def) . definitions)
- (put-uleb128 names-port def)
- (let ((tag (case representation
- ((scm) 0)
- ((f64) 1)
- ((u64) 2)
- ((s64) 3)
- ((ptr code) 4)
- (else (error "what!" representation)))))
- (put-uleb128 names-port (logior (ash slot 3) tag)))
- (lp definitions))))))
- (let lp ((metas metas) (pos arities-prefix-len) (relocs '()))
- (match metas
- (()
- (unless (= pos (bytevector-length headers))
- (error "expected to fully fill the bytevector"
- pos (bytevector-length headers)))
- relocs)
- ((meta . metas)
- (match (meta-arities meta)
- (() (lp metas pos relocs))
- ((arity)
- (lp metas
- (+ pos arity-header-len)
- (write-arity pos arity #f relocs)))
- (arities
- ;; Write a case-lambda header, then individual arities.
- ;; The case-lambda header's offset link is 0.
- (write-header pos (meta-low-pc meta) (meta-high-pc meta) 0
- (pack-arity-flags #f #f #f #t #f #f) 0 0 0)
- (let lp* ((arities arities) (pos (+ pos arity-header-len))
- (relocs relocs))
- (match arities
- (() (lp metas pos relocs))
- ((arity . arities)
- (lp* arities
- (+ pos arity-header-len)
- (write-arity pos arity #t relocs)))))))))))
- (define (link-arities asm)
- (define (meta-arities-header-size meta)
- (define (lambda-size arity)
- arity-header-len)
- (define (case-lambda-size arities)
- (fold +
- arity-header-len ;; case-lambda header
- (map lambda-size arities))) ;; the cases
- (match (meta-arities meta)
- (() 0)
- ((arity) (lambda-size arity))
- (arities (case-lambda-size arities))))
- (let* ((endianness (asm-endianness asm))
- (metas (reverse (asm-meta asm)))
- (header-size (fold (lambda (meta size)
- (+ size (meta-arities-header-size meta)))
- arities-prefix-len
- metas))
- (strtab (make-string-table))
- (headers (make-bytevector header-size 0)))
- (bytevector-u32-set! headers 0 (bytevector-length headers) endianness)
- (let-values (((names-port get-name-bv) (open-bytevector-output-port)))
- (let* ((relocs (write-arities asm metas headers names-port strtab))
- (name-bv (get-name-bv))
- (strtab (make-object asm '.guile.arities.strtab
- (string-table-size strtab)
- (string-table-writer strtab)
- '() '()
- #:type SHT_STRTAB #:flags 0)))
- (values (make-object asm '.guile.arities
- (+ header-size (bytevector-length name-bv))
- (lambda (bv)
- ;; FIXME: Avoid extra allocation + copy.
- (bytevector-copy! headers 0 bv 0
- header-size)
- (bytevector-copy! name-bv 0 bv header-size
- (bytevector-length name-bv)))
- relocs '()
- #:type SHT_PROGBITS #:flags 0
- #:link (elf-section-index
- (linker-object-section strtab)))
- strtab)))))
- ;;;
- ;;; The .guile.docstrs section is a packed, sorted array of (pc, str)
- ;;; values. Pc and str are both 32 bits wide. (Either could change to
- ;;; 64 bits if appropriate in the future.) Pc is the address of the
- ;;; entry to a program, relative to the start of the text section, in
- ;;; bytes, and str is an index into the associated .guile.docstrs.strtab
- ;;; string table section.
- ;;;
- ;; The size of a docstrs entry, in bytes.
- (define docstr-size 8)
- (define (link-docstrs asm)
- (define (find-docstrings)
- (filter-map (lambda (meta)
- (define (is-documentation? pair)
- (eq? (car pair) 'documentation))
- (let* ((props (meta-properties meta))
- (tail (find-tail is-documentation? props)))
- (and tail
- (not (find-tail is-documentation? (cdr tail)))
- (string? (cdar tail))
- (cons (meta-low-pc meta) (cdar tail)))))
- (reverse (asm-meta asm))))
- (let* ((endianness (asm-endianness asm))
- (strtab (make-string-table))
- (docstrings (map (match-lambda
- ((pc . str)
- (cons pc (string-table-intern! strtab str))))
- (find-docstrings))))
- (define (write-docstrings! bv)
- (fold (lambda (pair pos)
- (match pair
- ((pc . string-pos)
- (bytevector-u32-set! bv pos pc endianness)
- (bytevector-u32-set! bv (+ pos 4)
- string-pos
- endianness)
- (+ pos docstr-size))))
- 0
- docstrings))
- (let ((strtab (make-object asm '.guile.docstrs.strtab
- (string-table-size strtab)
- (string-table-writer strtab)
- '() '()
- #:type SHT_STRTAB #:flags 0)))
- (values (make-object asm '.guile.docstrs
- (* (length docstrings) docstr-size)
- write-docstrings!
- '() '()
- #:type SHT_PROGBITS #:flags 0
- #:link (elf-section-index
- (linker-object-section strtab)))
- strtab))))
- ;;;
- ;;; The .guile.procprops section is a packed, sorted array of (pc, addr)
- ;;; values. Pc and addr are both 32 bits wide. (Either could change to
- ;;; 64 bits if appropriate in the future.) Pc is the address of the
- ;;; entry to a program, relative to the start of the text section, and
- ;;; addr is the address of the associated properties alist, relative to
- ;;; the start of the ELF image.
- ;;;
- ;;; Since procedure properties are stored in the data sections, we need
- ;;; to link the procedures property section first. (Note that this
- ;;; constraint does not apply to the arities section, which may
- ;;; reference the data sections via the kw-indices literal, because
- ;;; assembling the text section already makes sure that the kw-indices
- ;;; are interned.)
- ;;;
- ;; The size of a procprops entry, in bytes.
- (define procprops-size 8)
- (define (link-procprops asm)
- (define (assoc-remove-one alist key value-pred)
- (match alist
- (() '())
- ((((? (lambda (x) (eq? x key))) . value) . alist)
- (if (value-pred value)
- alist
- (acons key value alist)))
- (((k . v) . alist)
- (acons k v (assoc-remove-one alist key value-pred)))))
- (define (props-without-name-or-docstring meta)
- (assoc-remove-one
- (assoc-remove-one (meta-properties meta) 'name (lambda (x) #t))
- 'documentation
- string?))
- (define (find-procprops)
- (filter-map (lambda (meta)
- (let ((props (props-without-name-or-docstring meta)))
- (and (pair? props)
- (cons (meta-low-pc meta) props))))
- (reverse (asm-meta asm))))
- (let* ((endianness (asm-endianness asm))
- (procprops (find-procprops))
- (size (* (length procprops) procprops-size)))
- (define (write-procprops! bv)
- (let lp ((procprops procprops) (pos 0))
- (match procprops
- (()
- #t)
- (((pc . props) . procprops)
- (bytevector-u32-set! bv pos pc endianness)
- (lp procprops (+ pos procprops-size))))))
- (define relocs
- (let lp ((procprops procprops) (pos 0) (relocs '()))
- (match procprops
- (()
- relocs)
- (((pc . props) . procprops)
- (lp procprops
- (+ pos procprops-size)
- (cons (make-linker-reloc 'abs32/1 (+ pos 4) 0
- (intern-constant asm props))
- relocs))))))
- (make-object asm '.guile.procprops
- size write-procprops!
- relocs '()
- #:type SHT_PROGBITS #:flags 0)))
- ;;;
- ;;; The DWARF .debug_info, .debug_abbrev, .debug_str, and .debug_loc
- ;;; sections provide line number and local variable liveness
- ;;; information. Their format is defined by the DWARF
- ;;; specifications.
- ;;;
- (define (asm-language asm)
- ;; FIXME: Plumb language through to the assembler.
- 'scheme)
- ;; -> 5 values: .debug_info, .debug_abbrev, .debug_str, .debug_loc, .debug_lines
- (define (link-debug asm)
- (define (put-s8 port val)
- (let ((bv (make-bytevector 1)))
- (bytevector-s8-set! bv 0 val)
- (put-bytevector port bv)))
- (define (put-u16 port val)
- (let ((bv (make-bytevector 2)))
- (bytevector-u16-set! bv 0 val (asm-endianness asm))
- (put-bytevector port bv)))
- (define (put-u32 port val)
- (let ((bv (make-bytevector 4)))
- (bytevector-u32-set! bv 0 val (asm-endianness asm))
- (put-bytevector port bv)))
- (define (put-u64 port val)
- (let ((bv (make-bytevector 8)))
- (bytevector-u64-set! bv 0 val (asm-endianness asm))
- (put-bytevector port bv)))
- (define (meta->subprogram-die meta)
- `(subprogram
- (@ ,@(cond
- ((meta-name meta)
- => (lambda (name) `((name ,(symbol->string name)))))
- (else
- '()))
- (low-pc ,(meta-label meta))
- (high-pc ,(- (meta-high-pc meta) (meta-low-pc meta))))))
- (define (make-compile-unit-die asm)
- `(compile-unit
- (@ (producer ,(string-append "Guile " (version)))
- (language ,(asm-language asm))
- (low-pc .rtl-text)
- (high-pc ,(asm-pos asm))
- (stmt-list 0))
- ,@(map meta->subprogram-die (reverse (asm-meta asm)))))
- (let-values (((die-port get-die-bv) (open-bytevector-output-port))
- ((die-relocs) '())
- ((abbrev-port get-abbrev-bv) (open-bytevector-output-port))
- ;; (tag has-kids? attrs forms) -> code
- ((abbrevs) vlist-null)
- ((strtab) (make-string-table))
- ((line-port get-line-bv) (open-bytevector-output-port))
- ((line-relocs) '())
- ;; file -> code
- ((files) vlist-null))
- (define (write-abbrev code tag has-children? attrs forms)
- (put-uleb128 abbrev-port code)
- (put-uleb128 abbrev-port (tag-name->code tag))
- (put-u8 abbrev-port (children-name->code (if has-children? 'yes 'no)))
- (for-each (lambda (attr form)
- (put-uleb128 abbrev-port (attribute-name->code attr))
- (put-uleb128 abbrev-port (form-name->code form)))
- attrs forms)
- (put-uleb128 abbrev-port 0)
- (put-uleb128 abbrev-port 0))
- (define (intern-abbrev tag has-children? attrs forms)
- (let ((key (list tag has-children? attrs forms)))
- (match (vhash-assoc key abbrevs)
- ((_ . code) code)
- (#f (let ((code (1+ (vlist-length abbrevs))))
- (set! abbrevs (vhash-cons key code abbrevs))
- (write-abbrev code tag has-children? attrs forms)
- code)))))
- (define (intern-file file)
- (match (vhash-assoc file files)
- ((_ . code) code)
- (#f (let ((code (1+ (vlist-length files))))
- (set! files (vhash-cons file code files))
- code))))
- (define (write-sources)
- ;; Choose line base and line range values that will allow for an
- ;; address advance range of 16 words. The special opcode range is
- ;; from 10 to 255, so 246 values.
- (define base -4)
- (define range 15)
- (define min-inc 4) ; Minimum PC increment.
- (let lp ((sources (asm-sources asm)) (out '()))
- (match sources
- (((pc . location) . sources)
- (let-values (((file line col)
- ;; Usually CPS records contain a "source
- ;; vector" coming from tree-il, but some might
- ;; contain a source property alist.
- (match location
- (#(file line col) (values file line col))
- (lst (values (assq-ref lst 'filename)
- (assq-ref lst 'line)
- (assq-ref lst 'column))))))
- (lp sources
- ;; Guile line and column numbers are 0-indexed, but
- ;; they are 1-indexed for DWARF.
- (if (and line col)
- (cons (list pc
- (if (string? file) (intern-file file) 0)
- (1+ line)
- (1+ col))
- out)
- out))))
- (()
- ;; Compilation unit header for .debug_line. We write in
- ;; DWARF 2 format because more tools understand it than DWARF
- ;; 4, which incompatibly adds another field to this header.
- (put-u32 line-port 0) ; Length; will patch later.
- (put-u16 line-port 2) ; DWARF 2 format.
- (put-u32 line-port 0) ; Prologue length; will patch later.
- (put-u8 line-port min-inc) ; Minimum instruction length: 4 bytes.
- (put-u8 line-port 1) ; Default is-stmt: true.
- (put-s8 line-port base) ; Line base. See the DWARF standard.
- (put-u8 line-port range) ; Line range. See the DWARF standard.
- (put-u8 line-port 10) ; Opcode base: the first "special" opcode.
- ;; A table of the number of uleb128 arguments taken by each
- ;; of the standard opcodes.
- (put-u8 line-port 0) ; 1: copy
- (put-u8 line-port 1) ; 2: advance-pc
- (put-u8 line-port 1) ; 3: advance-line
- (put-u8 line-port 1) ; 4: set-file
- (put-u8 line-port 1) ; 5: set-column
- (put-u8 line-port 0) ; 6: negate-stmt
- (put-u8 line-port 0) ; 7: set-basic-block
- (put-u8 line-port 0) ; 8: const-add-pc
- (put-u8 line-port 1) ; 9: fixed-advance-pc
- ;; Include directories, as a zero-terminated sequence of
- ;; nul-terminated strings. Nothing, for the moment.
- (put-u8 line-port 0)
- ;; File table. For each file that contributes to this
- ;; compilation unit, a nul-terminated file name string, and a
- ;; uleb128 for each of directory the file was found in, the
- ;; modification time, and the file's size in bytes. We pass
- ;; zero for the latter three fields.
- (vlist-fold-right
- (lambda (pair seed)
- (match pair
- ((file . code)
- (put-bytevector line-port (string->utf8 file))
- (put-u8 line-port 0)
- (put-uleb128 line-port 0) ; directory
- (put-uleb128 line-port 0) ; mtime
- (put-uleb128 line-port 0))) ; size
- seed)
- #f
- files)
- (put-u8 line-port 0) ; 0 byte terminating file list.
- ;; Patch prologue length.
- (let ((offset (port-position line-port)))
- (seek line-port 6 SEEK_SET)
- (put-u32 line-port (- offset 10))
- (seek line-port offset SEEK_SET))
- ;; Now write the statement program.
- (let ()
- (define (extended-op opcode payload-len)
- (put-u8 line-port 0) ; extended op
- (put-uleb128 line-port (1+ payload-len)) ; payload-len + opcode
- (put-uleb128 line-port opcode))
- (define (set-address sym)
- (define (add-reloc! kind)
- (set! line-relocs
- (cons (make-linker-reloc kind
- (port-position line-port)
- 0
- sym)
- line-relocs)))
- (match (asm-word-size asm)
- (4
- (extended-op 2 4)
- (add-reloc! 'abs32/1)
- (put-u32 line-port 0))
- (8
- (extended-op 2 8)
- (add-reloc! 'abs64/1)
- (put-u64 line-port 0))))
- (define (end-sequence pc)
- (let ((pc-inc (/ (- (asm-pos asm) pc) min-inc)))
- (put-u8 line-port 2) ; advance-pc
- (put-uleb128 line-port pc-inc))
- (extended-op 1 0))
- (define (advance-pc pc-inc line-inc)
- (let ((spec (+ (- line-inc base)
- (* (/ pc-inc min-inc) range)
- 10)))
- (cond
- ((or (< line-inc base) (>= line-inc (+ base range)))
- (advance-line line-inc)
- (advance-pc pc-inc 0))
- ((<= spec 255)
- (put-u8 line-port spec))
- ((< spec 500)
- (put-u8 line-port 8) ; const-advance-pc
- (advance-pc (- pc-inc (* (floor/ (- 255 10) range) min-inc))
- line-inc))
- (else
- (put-u8 line-port 2) ; advance-pc
- (put-uleb128 line-port (/ pc-inc min-inc))
- (advance-pc 0 line-inc)))))
- (define (advance-line inc)
- (put-u8 line-port 3)
- (put-sleb128 line-port inc))
- (define (set-file file)
- (put-u8 line-port 4)
- (put-uleb128 line-port file))
- (define (set-column col)
- (put-u8 line-port 5)
- (put-uleb128 line-port col))
- (set-address '.rtl-text)
- (let lp ((in out) (pc 0) (file 1) (line 1) (col 0))
- (match in
- (()
- (when (null? out)
- ;; There was no source info in the first place. Set
- ;; file register to 0 before adding final row.
- (set-file 0))
- (end-sequence pc))
- (((pc* file* line* col*) . in*)
- (cond
- ((and (eqv? file file*) (eqv? line line*) (eqv? col col*))
- (lp in* pc file line col))
- (else
- (unless (eqv? col col*)
- (set-column col*))
- (unless (eqv? file file*)
- (set-file file*))
- (advance-pc (- pc* pc) (- line* line))
- (lp in* pc* file* line* col*)))))))))))
- (define (compute-code attr val)
- (match attr
- ('name (string-table-intern! strtab val))
- ('low-pc val)
- ('high-pc val)
- ('producer (string-table-intern! strtab val))
- ('language (language-name->code val))
- ('stmt-list val)))
- (define (choose-form attr val code)
- (cond
- ((string? val) 'strp)
- ((eq? attr 'stmt-list) 'sec-offset)
- ((eq? attr 'low-pc) 'addr)
- ((exact-integer? code)
- (cond
- ((< code 0) 'sleb128)
- ((<= code #xff) 'data1)
- ((<= code #xffff) 'data2)
- ((<= code #xffffffff) 'data4)
- ((<= code #xffffffffffffffff) 'data8)
- (else 'uleb128)))
- (else (error "unhandled case" attr val code))))
- (define (add-die-relocation! kind sym)
- (set! die-relocs
- (cons (make-linker-reloc kind (port-position die-port) 0 sym)
- die-relocs)))
- (define (write-value code form)
- (match form
- ('data1 (put-u8 die-port code))
- ('data2 (put-u16 die-port code))
- ('data4 (put-u32 die-port code))
- ('data8 (put-u64 die-port code))
- ('uleb128 (put-uleb128 die-port code))
- ('sleb128 (put-sleb128 die-port code))
- ('addr
- (match (asm-word-size asm)
- (4
- (add-die-relocation! 'abs32/1 code)
- (put-u32 die-port 0))
- (8
- (add-die-relocation! 'abs64/1 code)
- (put-u64 die-port 0))))
- ('sec-offset (put-u32 die-port code))
- ('strp (put-u32 die-port code))))
- (define (write-die die)
- (match die
- ((tag ('@ (attrs vals) ...) children ...)
- (let* ((codes (map compute-code attrs vals))
- (forms (map choose-form attrs vals codes))
- (has-children? (not (null? children)))
- (abbrev-code (intern-abbrev tag has-children? attrs forms)))
- (put-uleb128 die-port abbrev-code)
- (for-each write-value codes forms)
- (when has-children?
- (for-each write-die children)
- (put-uleb128 die-port 0))))))
- (define (copy-writer source)
- (lambda (bv)
- (bytevector-copy! source 0 bv 0
- (bytevector-length source))))
- ;; Compilation unit header.
- (put-u32 die-port 0) ; Length; will patch later.
- (put-u16 die-port 4) ; DWARF 4.
- (put-u32 die-port 0) ; Abbrevs offset.
- (put-u8 die-port (asm-word-size asm)) ; Address size.
- (write-die (make-compile-unit-die asm))
- ;; Terminate the abbrevs list.
- (put-uleb128 abbrev-port 0)
- (write-sources)
- (values (let ((bv (get-die-bv)))
- ;; Patch DWARF32 length.
- (bytevector-u32-set! bv 0 (- (bytevector-length bv) 4)
- (asm-endianness asm))
- (make-object asm '.debug_info
- (bytevector-length bv)
- (copy-writer bv)
- die-relocs '()
- #:type SHT_PROGBITS #:flags 0))
- (let ((bv (get-abbrev-bv)))
- (make-object asm '.debug_abbrev
- (bytevector-length bv) (copy-writer bv)
- '() '()
- #:type SHT_PROGBITS #:flags 0))
- (make-object asm '.debug_str
- (string-table-size strtab)
- (string-table-writer strtab)
- '() '()
- #:type SHT_PROGBITS #:flags 0)
- (make-object asm '.debug_loc
- 0 (lambda (bv) #t)
- '() '()
- #:type SHT_PROGBITS #:flags 0)
- (let ((bv (get-line-bv)))
- ;; Patch DWARF32 length.
- (bytevector-u32-set! bv 0 (- (bytevector-length bv) 4)
- (asm-endianness asm))
- (make-object asm '.debug_line
- (bytevector-length bv) (copy-writer bv)
- line-relocs '()
- #:type SHT_PROGBITS #:flags 0)))))
- (define (link-objects asm)
- (let*-values (;; Link procprops before constants, because it probably
- ;; interns more constants.
- ((procprops) (link-procprops asm))
- ((ro rw rw-init) (link-constants asm))
- ;; Link text object after constants, so that the
- ;; constants initializer gets included.
- ((text) (link-text-object asm))
- ((frame-maps) (link-frame-maps asm))
- ((dt) (link-dynamic-section asm text rw rw-init frame-maps))
- ((symtab strtab) (link-symtab (linker-object-section text) asm))
- ((arities arities-strtab) (link-arities asm))
- ((docstrs docstrs-strtab) (link-docstrs asm))
- ((dinfo dabbrev dstrtab dloc dline) (link-debug asm))
- ;; This needs to be linked last, because linking other
- ;; sections adds entries to the string table.
- ((shstrtab) (link-shstrtab asm)))
- (filter identity
- (list text ro frame-maps rw dt symtab strtab
- arities arities-strtab
- docstrs docstrs-strtab procprops
- dinfo dabbrev dstrtab dloc dline
- shstrtab))))
- ;;;
- ;;; High-level public interfaces.
- ;;;
- (define* (link-assembly asm #:key (page-aligned? #t))
- "Produce an ELF image from the code and data emitted into @var{asm}.
- The result is a bytevector, by default linked so that read-only and
- writable data are on separate pages. Pass @code{#:page-aligned? #f} to
- disable this behavior."
- (link-elf (link-objects asm) #:page-aligned? page-aligned?))
|