factor/basis/bootstrap/image/image.factor

549 lines
14 KiB
Factor
Executable File

! Copyright (C) 2004, 2011 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: accessors arrays assocs bootstrap.image.syntax
byte-arrays classes classes.builtin classes.private
classes.tuple classes.tuple.private combinators
combinators.short-circuit combinators.smart
compiler.codegen.relocation compiler.units fry generic
generic.single.private grouping hashtables hashtables.private io
io.binary io.encodings.binary io.files io.pathnames kernel
kernel.private layouts locals make math math.order namespaces
namespaces.private parser parser.notes prettyprint quotations
sequences sequences.private source-files strings system vectors
vocabs words ;
IN: bootstrap.image
: arch-name ( os cpu -- arch )
2dup [ windows? ] [ ppc? ] bi* or [
[ drop unix ] dip
] unless
[ name>> ] bi@ "-" glue ;
: my-arch-name ( -- arch )
os cpu arch-name ;
: boot-image-name ( arch -- string )
"boot." ".image" surround ;
: my-boot-image-name ( -- string )
my-arch-name boot-image-name ;
CONSTANT: image-names
{
"windows-x86.32" "unix-x86.32"
"windows-x86.64" "unix-x86.64"
}
<PRIVATE
! Object cache; we only consider numbers equal if they have the
! same type
TUPLE: eql-wrapper { obj read-only } ;
C: <eql-wrapper> eql-wrapper
M: eql-wrapper hashcode* obj>> hashcode* ;
GENERIC: (eql?) ( obj1 obj2 -- ? )
: eql? ( obj1 obj2 -- ? )
{ [ [ class-of ] same? ] [ (eql?) ] } 2&& ;
M: fixnum (eql?) eq? ;
M: bignum (eql?) { bignum bignum } declare = ;
M: float (eql?) fp-bitwise= ;
M: sequence (eql?)
2dup [ length ] same? [ [ eql? ] 2all? ] [ 2drop f ] if ;
M: object (eql?) = ;
M: eql-wrapper equal?
over eql-wrapper? [ [ obj>> ] bi@ eql? ] [ 2drop f ] if ;
TUPLE: eq-wrapper { obj read-only } ;
C: <eq-wrapper> eq-wrapper
M: eq-wrapper equal?
over eq-wrapper? [ [ obj>> ] bi@ eq? ] [ 2drop f ] if ;
M: eq-wrapper hashcode*
nip obj>> identity-hashcode ;
SYMBOL: objects
: cache-eql-object ( obj quot -- value )
[ <eql-wrapper> objects get ] dip '[ obj>> @ ] cache ; inline
: cache-eq-object ( obj quot -- value )
[ <eq-wrapper> objects get ] dip '[ obj>> @ ] cache ; inline
: lookup-object ( obj -- n/f )
<eq-wrapper> objects get at ;
: put-object ( n obj -- )
<eq-wrapper> objects get set-at ;
! Constants
CONSTANT: image-magic 0x0f0e0d0c
CONSTANT: image-version 4
CONSTANT: data-base 1024
CONSTANT: special-objects-size 80
CONSTANT: header-size 10
CONSTANT: data-heap-size-offset 3
CONSTANT: t-offset 6
CONSTANT: 0-offset 7
CONSTANT: 1-offset 8
CONSTANT: -1-offset 9
SYMBOL: sub-primitives
SYMBOL: bs-special-objects
:: jit-conditional ( test-quot false-quot -- )
[ 0 test-quot call ] B{ } make length :> len
building get length extra-offset get + len +
[ extra-offset set false-quot call ] B{ } make
[ length test-quot call ] [ % ] bi ; inline
: make-jit ( quot -- parameters literals code )
#! code is a { relocation insns } pair
[
0 extra-offset set
init-relocation
call( -- )
parameter-table get >array
literal-table get >array
relocation-table get >byte-array
] B{ } make 2array ;
: make-jit-no-params ( quot -- code )
make-jit 2nip ;
: jit-define ( quot n -- )
[ make-jit-no-params ] dip bs-special-objects get set-at ;
: define-sub-primitive ( quot word -- )
[ make-jit 3array ] dip sub-primitives get set-at ;
: define-combinator-primitive ( quot non-tail-quot tail-quot word -- )
[
[
[ make-jit ]
[ make-jit-no-params ]
[ make-jit-no-params ]
tri*
] output>array
] dip
sub-primitives get set-at ;
SYMBOL: bootstrapping-image
! Image output format
SYMBOL: big-endian
SYMBOL: architecture
H{ } clone bs-special-objects set-global
: emit ( cell -- ) bootstrapping-image get push ;
: emit-64 ( cell -- )
bootstrap-cell 8 = [
emit
] [
d>w/w big-endian get [ swap ] unless emit emit
] if ;
: emit-seq ( seq -- ) bootstrapping-image get push-all ;
: fixup ( value offset -- ) bootstrapping-image get set-nth ;
: heap-size ( -- size )
bootstrapping-image get length header-size - special-objects-size -
bootstrap-cells ;
: here ( -- size ) heap-size data-base + ;
: here-as ( tag -- pointer ) here bitor ;
: (align-here) ( alignment -- )
[ here neg ] dip rem
[ bootstrap-cell /i [ 0 emit ] times ] unless-zero ;
: align-here ( -- )
data-alignment get (align-here) ;
: emit-fixnum ( n -- ) tag-fixnum emit ;
: emit-header ( n -- ) tag-header emit ;
: emit-object ( class quot -- addr )
[ type-number ] dip over here-as
[ swap emit-header call align-here ] dip ; inline
! Write an object to the image.
GENERIC: ' ( obj -- ptr )
! Image header
: emit-image-header ( -- )
image-magic emit
image-version emit
data-base emit ! relocation base at end of header
0 emit ! size of data heap set later
0 emit ! reloc base of code heap is 0
0 emit ! size of code heap is 0
0 emit ! pointer to t object
0 emit ! pointer to bignum 0
0 emit ! pointer to bignum 1
0 emit ! pointer to bignum -1
special-objects-size [ f ' emit ] times ;
! Bignums
: bignum-bits ( -- n ) bootstrap-cell-bits 2 - ;
: bignum-radix ( -- n ) bignum-bits 2^ 1 - ;
: bignum>sequence ( n -- seq )
#! n is positive or zero.
[ dup 0 > ]
[ [ bignum-bits neg shift ] [ bignum-radix bitand ] bi ]
produce nip ;
: emit-bignum ( n -- )
dup dup 0 < [ neg ] when bignum>sequence
[ nip length 1 + emit-fixnum ]
[ drop 0 < 1 0 ? emit ]
[ nip emit-seq ]
2tri ;
M: bignum '
[
bignum [ emit-bignum ] emit-object
] cache-eql-object ;
! Fixnums
M: fixnum '
#! When generating a 32-bit image on a 64-bit system,
#! some fixnums should be bignums.
dup
bootstrap-most-negative-fixnum
bootstrap-most-positive-fixnum between?
[ tag-fixnum ] [ >bignum ' ] if ;
TUPLE: fake-bignum n ;
C: <fake-bignum> fake-bignum
M: fake-bignum ' n>> tag-fixnum ;
! Floats
M: float '
[
float [
8 (align-here) double>bits emit-64
] emit-object
] cache-eql-object ;
! Special objects
! Padded with fixnums for 8-byte alignment
: t, ( -- ) t t-offset fixup ;
M: f ' drop \ f type-number ;
: 0, ( -- ) 0 >bignum ' 0-offset fixup ;
: 1, ( -- ) 1 >bignum ' 1-offset fixup ;
: -1, ( -- ) -1 >bignum ' -1-offset fixup ;
! Words
: word-sub-primitive ( word -- obj )
[ target-word ] with-global sub-primitives get at ;
: emit-word ( word -- )
[
[ subwords [ emit-word ] each ]
[
[
{
[ hashcode <fake-bignum> ]
[ name>> ]
[ vocabulary>> ]
[ def>> ]
[ props>> ]
[ pic-def>> ]
[ pic-tail-def>> ]
[ word-sub-primitive ]
[ drop 0 ] ! entry point
} cleave
] output>array [ ' ] map!
] bi
\ word [ emit-seq ] emit-object
] keep put-object ;
ERROR: not-in-image vocabulary word ;
: transfer-word ( word -- word )
[ target-word ] keep or ;
: fixup-word ( word -- offset )
transfer-word dup lookup-object
[ ] [ [ vocabulary>> ] [ name>> ] bi not-in-image ] ?if ;
: fixup-words ( -- )
bootstrapping-image get [ dup word? [ fixup-word ] when ] map! drop ;
M: word ' ;
! Wrappers
M: wrapper '
[ wrapped>> ' wrapper [ emit ] emit-object ] cache-eql-object ;
! Strings
: native> ( object -- object )
big-endian get [ [ be> ] map ] [ [ le> ] map ] if ;
: emit-bytes ( seq -- )
bootstrap-cell <groups> native> emit-seq ;
: pad-bytes ( seq -- newseq )
dup length bootstrap-cell align 0 pad-tail ;
: extended-part ( str -- str' )
dup [ 128 < ] all? [ drop f ] [
[ -7 shift 1 bitxor ] { } map-as
big-endian get
[ [ 2 >be ] { } map-as ]
[ [ 2 >le ] { } map-as ] if
B{ } join
] if ;
: ascii-part ( str -- str' )
[
[ 128 mod ] [ 128 >= ] bi
[ 128 bitor ] when
] B{ } map-as ;
: emit-string ( string -- ptr )
[ length ] [ extended-part ' ] [ ] tri
string [
[ emit-fixnum ]
[ emit ]
[ f ' emit ascii-part pad-bytes emit-bytes ]
tri*
] emit-object ;
M: string '
#! We pool strings so that each string is only written once
#! to the image
[ emit-string ] cache-eql-object ;
: assert-empty ( seq -- )
length 0 assert= ;
: emit-dummy-array ( obj type -- ptr )
[ assert-empty ] [
[ 0 emit-fixnum ] emit-object
] bi* ;
M: byte-array '
[
byte-array [
dup length emit-fixnum
bootstrap-cell 4 = [ 0 emit 0 emit ] when
pad-bytes emit-bytes
] emit-object
] cache-eq-object ;
! Tuples
ERROR: tuple-removed class ;
: require-tuple-layout ( word -- layout )
dup tuple-layout [ ] [ tuple-removed ] ?if ;
: (emit-tuple) ( tuple -- pointer )
[ tuple-slots ]
[ class-of transfer-word require-tuple-layout ] bi prefix [ ' ] map
tuple [ emit-seq ] emit-object ;
: emit-tuple ( tuple -- pointer )
dup class-of name>> "tombstone" =
[ [ (emit-tuple) ] cache-eql-object ]
[ [ (emit-tuple) ] cache-eq-object ]
if ;
M: tuple ' emit-tuple ;
M: tombstone '
state>> "((tombstone))" "((empty))" ?
"hashtables.private" lookup-word def>> first
[ emit-tuple ] cache-eql-object ;
! Arrays
: emit-array ( array -- offset )
[ ' ] map array [ [ length emit-fixnum ] [ emit-seq ] bi ] emit-object ;
M: array ' [ emit-array ] cache-eq-object ;
! This is a hack. We need to detect arrays which are tuple
! layout arrays so that they can be internalized, but making
! them a built-in type is not worth it.
PREDICATE: tuple-layout-array < array
dup length 5 >= [
{
[ first-unsafe tuple-class? ]
[ second-unsafe fixnum? ]
[ third-unsafe fixnum? ]
} 1&&
] [ drop f ] if ;
M: tuple-layout-array '
[
[ dup integer? [ <fake-bignum> ] when ] map
emit-array
] cache-eql-object ;
! Quotations
M: quotation '
[
array>> '
quotation [
emit ! array
f ' emit ! cached-effect
f ' emit ! cache-counter
0 emit ! entry point
] emit-object
] cache-eql-object ;
! End of the image
: emit-words ( -- )
all-words [ emit-word ] each ;
: emit-global ( -- )
{
dictionary source-files builtins
update-map implementors-map
} [ [ bootstrap-word ] [ get global-box boa ] bi ] H{ } map>assoc
{
class<=-cache class-not-cache classes-intersect-cache
class-and-cache class-or-cache next-method-quot-cache
} [ H{ } clone global-box boa ] H{ } map>assoc assoc-union
global-hashtable boa
OBJ-GLOBAL bs-special-objects get set-at ;
: emit-jit-data ( -- )
{
{ JIT-IF-WORD if }
{ JIT-PRIMITIVE-WORD do-primitive }
{ JIT-DIP-WORD dip }
{ JIT-2DIP-WORD 2dip }
{ JIT-3DIP-WORD 3dip }
{ PIC-MISS-WORD inline-cache-miss }
{ PIC-MISS-TAIL-WORD inline-cache-miss-tail }
{ MEGA-LOOKUP-WORD mega-cache-lookup }
{ MEGA-MISS-WORD mega-cache-miss }
{ JIT-DECLARE-WORD declare }
{ C-TO-FACTOR-WORD c-to-factor }
{ LAZY-JIT-COMPILE-WORD lazy-jit-compile }
{ UNWIND-NATIVE-FRAMES-WORD unwind-native-frames }
{ GET-FPU-STATE-WORD fpu-state }
{ SET-FPU-STATE-WORD set-fpu-state }
{ SIGNAL-HANDLER-WORD signal-handler }
{ LEAF-SIGNAL-HANDLER-WORD leaf-signal-handler }
{ FFI-SIGNAL-HANDLER-WORD ffi-signal-handler }
{ FFI-LEAF-SIGNAL-HANDLER-WORD ffi-leaf-signal-handler }
}
\ OBJ-UNDEFINED undefined-def 2array suffix [
swap execute( -- x ) bs-special-objects get set-at
] assoc-each ;
: emit-special-object ( obj idx -- )
[ ' ] [ header-size + ] bi* fixup ;
: emit-special-objects ( -- )
bs-special-objects get [
swap emit-special-object
] assoc-each ;
: fixup-header ( -- )
heap-size data-heap-size-offset fixup ;
: build-generics ( -- )
[
all-words
[ generic? ] filter
[ make-generic ] each
] with-compilation-unit ;
: build-image ( -- image )
600,000 <vector> bootstrapping-image set
60,000 <hashtable> objects set
emit-image-header t, 0, 1, -1,
"Building generic words..." print flush
build-generics
"Serializing words..." print flush
emit-words
"Serializing JIT data..." print flush
emit-jit-data
"Serializing global namespace..." print flush
emit-global
"Serializing special object table..." print flush
emit-special-objects
"Performing word fixups..." print flush
fixup-words
"Performing header fixups..." print flush
fixup-header
"Image length: " write bootstrapping-image get length .
"Object cache size: " write objects get assoc-size .
\ last-word global delete-at
bootstrapping-image get ;
! Image output
: (write-image) ( image -- )
bootstrap-cell output-stream get
big-endian get
[ '[ _ >be _ stream-write ] each ]
[ '[ _ >le _ stream-write ] each ] if ;
: write-image ( image -- )
"Writing image to " write
architecture get boot-image-name resource-path
[ write "..." print flush ]
[ binary [ (write-image) ] with-file-writer ] bi ;
PRIVATE>
: make-image ( arch -- )
architecture associate H{
{ parser-quiet? f }
{ auto-use? f }
} assoc-union! [
"resource:/core/bootstrap/stage1.factor" run-file
build-image
write-image
] with-variables ;
: make-images ( -- )
image-names [ make-image ] each ;
: make-my-image ( -- )
my-arch-name make-image ;