Magic subdirectory of the C PamCase
compiler for the Napier88 persistent programming language. See the
0magic.nw file in the parent directory for further
information about the C PamCase compiler.
*0magic.nw is the file from which this document is derived, and is
the noweb markup. (Of course, if you are reading this markup file
directly, then please don't be confused by this distinction.)
<Magic ls>= 0magic.fw Makefile initMagic.N cCompile.N exportAny.N magic.N
This code is written to a file (or else not used).
Makefile for the Magic subdirectory. It was
created on 26 Oct 1994 by John Hurst, and modified for this noweb
document on 22 Aug 1995, also by John Hurst.
The variable init defines the initialization routine for the
procedures defined in this subdirectory, and the variable files
defines the files containing the definitions for those procedures.
These variables are used by the included makefile, MakefileNapier.
The cpif calls (copy if) are included to avoid regenerating
unchanged .N files and causing make to do extra work.
cpif takes standard input, compares it to the argument file, and
rewrites it if the condition (here -ne for not equal) is met.
<Makefile>=
# define all the components in this directory, both the locs where they go,
# and the links to their values.
files := cCompile magic exportAny
init := initMagic
include $(CPAMC)/MakefileNapier
Makefile: 0magic.nw
notangle -RMakefile -t8 0magic.nw | cpif -ne Makefile
cCompile.N: 0magic.nw
notangle -RcCompile.N 0magic.nw | cpif -ne cCompile.N
exportAny.N: 0magic.nw
notangle -RexportAny.N 0magic.nw | cpif -ne exportAny.N
magic.N: 0magic.nw
notangle -Rmagic.N 0magic.nw | cpif -ne magic.N
This code is written to a file (or else not used).
initMagic.N for the CPamCASE compiler was first created
on 26 Oct 1994 by John Hurst from an equivalent version for the
IntPamCASE compiler by Quintin Cutts. It was modified for this noweb
document on 22 Aug 1995, also by John Hurst.
This is the initialization procedure for the Magic subdirectory of the
C code generation part of the compiler. All this program does is to
remove any existing definitions of the load subdirectory components, and
install new templates. Each of these templates is initialized with a
dummy value. The actual components are defined in the other .N
files generated in the other sections of this document.
Note that the initial values given to the defined components serve
only to print a warning message, and return a dummy value of the
appropriate type.
<initMagic.N>=
! Dependencies : -t boot -t passes -t cPamCaseCGPass
project PS() as root onto env :
use root with Library : env in
use Library with Environment : env;
getImplementation : proc(string -> env) in
use Environment with environment : proc(-> env) in
use getImplementation("wizard") with CompilerImplementation : env in
use CompilerImplementation with General,Source,Passes : env in
use General with initMsg : proc(string) in
use Passes with l1L2ICManager : L1L2ICManager ;
scopingModule : L2ScopingModule ;
CPamCaseCG : env in
use l1L2ICManager as ICM[IC,CH,Pos] in
use scopingModule as SM[Scope] in
begin
!*** Specialise some types
type SpecCPamCaseCGResult is CPamCaseCGResult[IC,Scope]
type SpecCPamCaseCGSuccess is CPamCaseCGSuccess[IC,Scope]
type SpecCPamCaseCGState is CPamCaseCGState[IC,Scope]
begin
if CPamCaseCG contains Magic do drop Magic from CPamCaseCG
let Magic = environment()
in CPamCaseCG let Magic = Magic
in Magic let exportAny := proc(filename : string; value : any)
initMsg("exportAny")
in Magic let cCompile := proc(state:SpecCPamCaseCGState -> *CPamInst)
{initMsg("cCompile"); vector @1 of [CPamInst(pam:0)]}
in Magic let constructCode :=
proc(state : SpecCPamCaseCGState -> CodeObject)
{initMsg("constructCode"); CodeObject(nil,nil)}
in Magic let magic := proc(code : NCode -> CodeObject)
{initMsg("magic"); CodeObject(nil,nil)}
end
end
default : {}
?
This code is written to a file (or else not used).
noweb
documented version.
<cCompile.N>=
! Dependencies : -t general -t boot -t tables -t -t compilerGeneral -t compilerErrors -t LA -t passes -t cPamCaseCGPass -t hyperLink
project PS() as root onto env :
use root with Library : env in
use Library with Format,IO,String,Tables : env;
getImplementation : proc(string -> env) in
use Format with iformat : proc(int -> string) in
use IO with
PrimitiveIO : env;
makeReadEnv : proc(file->env);
makeWriteEnv : proc(file->env);
writeString : proc(string) in
use PrimitiveIO with
create : proc(string,int -> file);
open : proc(string,int -> file);
close : proc(file -> int) in
use String with length : proc(string -> int) in
use Tables with
tableGen : proc[Key,Data](Comparison[Key] -> Table[Key,Data]) in
use getImplementation("wizard") with CompilerImplementation : env in
use CompilerImplementation with
Errors,General,LA,Source,Passes,Types : env in
use Errors with Compilation : env in
use Compilation with Error : proc(string) in
use General with Symbols : env in
use Symbols with eot : string in
use LA with lexicalAnalyserGen : LAGenerator in
use Source with IC : env in
use Passes with l1L2ICManager : L1L2ICManager;
scopingModule : L2ScopingModule;
mkSymbolTable : proc(-> SymbolTable);
CPamCaseCG : env in
use CPamCaseCG with LexProcs,CGConstants,Magic : env in
use LexProcs with nextSy : proc(LexInfoState) in
use CGConstants with
cvecPntrOverhead, initCVec, pEnvStart, pLitStart,
sEnvStart, stSize, stSize2, stSize3 : int in
use Magic with
exportAny : proc(string,any);
magic : proc(NCode -> CodeObject) in
use l1L2ICManager as ICM[IC,CH,Pos] in
use scopingModule as SM[Scope] in
use Types with UNDEFINED : TypeRep in
begin
!*** Specialise some types
type SpecCPamCaseCGResult is CPamCaseCGResult[IC,Scope]
type SpecCPamCaseCGSuccess is CPamCaseCGSuccess[IC,Scope]
type SpecCPamCaseCGState is CPamCaseCGState[IC,Scope]
use Magic with
cCompile : proc(SpecCPamCaseCGState -> *CPamInst);
constructCode : proc(SpecCPamCaseCGState -> CodeObject) in
begin
<cCompile definition>
<constructCode definition>
end
end
default : {}
?
This code is written to a file (or else not used).
cCompile is called to perform a C compilation against the current
state. It assumes that the code vector in the parameter state is
a sequence of CPamInst'macro variants.
These macros are formatted and written into a file, together with
appropriate header and trailer information, which is then passed to
the gcc C compiler for compilation. The header and trailer files
serve to package the macro code, and form the resultant program into a
self-extracting program, written in C, and saved in the file known as
pm.c.
The code is written as a self-extracting program because of the need
to develop pure code, free from the extraneous material added by the C
compiler in forming a standalone program. Relocatable code or .o
files are also not sufficient here, because of the relocation
information.
To this end, the procedure being compiled has a main routine added,
which performs the function of extracting the required code. When the
program formed from compiling pm.c is executed, the main routine
determines the boundaries of the procedure, and writes the code that
it finds between those boundaries into another file, always known by
the filename pm.raw
Once the code has been extracted, cCompile reads this raw code
back, and places it into a code vector. This vector is returned as a
sequence of integers in the CPamInst'pam variant.
<cCompile definition>=
cCompile := proc(state:SpecCPamCaseCGState -> *CPamInst)
begin
<int2str definition>
let mklabel = proc(lab : int -> string); "L"++int2str(lab,3)
<macroString definition>
<writeMacroFile definition>
<doCompile definition>
<readRawCode definition>
let cv := state(cvec)
let cvcount = state(cp)-1
writeString("*** Found "++iformat(cvcount)++" instructions'n")
let stem = "pm"
let macrofile = stem ++ ".c"
let nativefile = stem ++ ".raw"
!*** now start the business of writing the macros into a file
writeMacroFile(macrofile,cv,cvcount)
!*** now C compile the resulting file and run to get nativecode
let cCodeError := doCCompile(macrofile,nativefile)
!*** read the raw code back into a vector, and return that
if ~ cCodeError then readRawCode(nativefile)
else vector @1 of [CPamInst(pam:0)] ! return noop if fail
end
Used above.
int2str returns the string representation of the n in w
digits, with 0 left fill as necessary. If w is too small, it is ignored.
<int2str definition>=
let int2str = proc(n,w:int -> string)
begin
let res := iformat(n)
let l = length(res)
if l<w do begin
let pad = "00000000000000000000" ! 20 of them should be enough
res := pad(1|w-l)++res
end
res
end
Used above.
macroString converts a CPamMacro argument m into its
string representation. This is in three parts: the label (if any),
the macro name, and the parameters to the macro.
<macroString definition>= let macroString = proc(m : CPamMacro -> string); begin <compute macro label> <compute macro name> <compute macro parameters> label++name++parms end
DefinesmacroString(links are to index).Used above.
The label part is easy: if the value of the label is 0, issue no
label, else convert the value to a string, and append a colon. The
label representation of label $n$ is ``L$n$''.
<compute macro label>=
label = if m(label)=0 then " "
else mklabel(m(label))++":"
Used above.
Macro names come in two forms, depending on the variant. If the
variant is pamop, then the macro is a number $n$, and the string
representation is ``Pam$n$''. If the variant is a direct name,
then just that name is used without further conversion.
<compute macro name>=
let name =
project m(name) as mn onto
pamop : "Pam"++int2str(mn,3)
direct : mn
default : "bad_MacroName"
Used above.
If parameters are present, then the vector of parameters is output as a comma separated list. The values of parameters can be either integers (output directly) or labels, in which case a label representation is output.
<compute macro parameters>=
let parms := ""; let sep := "("
let args = m(arguments)
if args is present then begin
let argpres = args'present
for i=1 to upb[Argument](argpres) do begin
parms := parms++sep; sep := ","
parms := parms++(project argpres(i) as argv onto
intarg:iformat(argv)
cvloc:mklabel(argv)
default:"bad_argument")
end
end else parms := parms++sep
parms := parms++")"
Used above.
<writeMacroFile definition>=
let writeMacroFile = proc(cname : string; cv : *CPamInst; cvcount : int)
begin
let newfile = create(cname,6*64+6*8+4)
let macrofile = open("DISK:"++cname,1)
if macrofile=nilfile do Error("cannot open macros file")
let macroenv = makeWriteEnv(macrofile)
use macroenv with writeString : proc(string) in
begin
<write macro file header information>
<write macro file macro calls>
<write macro file trailer information>
end
if close(macrofile)=-1 do Error("cannot close macros file")
end ! writeMacroFile
Used above.
The main information conveyed in the header is the name of the include file containing all the macro definitions.
<write macro file header information>=
writeString("#include '"cpc-codegen-1.h'"'n")
writeString("N4_Return_t Capsule_1()'n{'n")
Used above.
<write macro file macro calls>=
for i=1 to cvcount do begin
let inst = cv(i)
project inst as X onto
pam:writeString(":Pam byte "++iformat(X))
macro:writeString(macroString(X))
default:writeString("*** cCompile:Illegal variant in PamInst")
writeString("'n")
end
Used above.
<write macro file trailer information>=
writeString("}'n")
writeString("void'n")
writeString("Capsule_1_Follow()'n")
writeString("{'n")
writeString("}'n")
writeString("MAIN_BEGIN'n")
writeString(" SAVE_CAPSULE(1)'n")
writeString("MAIN_END'n")
Used above.
doCompile is the procedure which actually does the C compilation
process. We handle this by creating a subprocess, and attaching a
write (shellWenv) and read (shellRenv) environment to the
standard input and output (respectively) of the subprocess.
compileError equal to true flags that the C compilation was
unsuccessful. A few aliases and bindings help in this process.
<doCompile definition>=
let doCCompile = proc(cname,rawname : string -> bool)
begin
let compileError := false
let shellfile = open("SHELL:",2)
let shellWenv = makeWriteEnv(shellfile)
let shellRenv = makeReadEnv(shellfile)
let ws = writeString
use shellWenv with writeString : proc(string) in
use shellRenv with
endOfInput : proc(-> bool);
readLine : proc(-> string);
readString : proc(-> string) in
Used above; next definition.
The endmessage allows us to signal the end of the control stream
into the subprocess.
<doCompile definition>+=
begin
let endmessage = "THE END"
let emLen = length(endmessage)
<pattern matching procedure definition>
writeString("/bin/rm "++rawname++"'n")
Used above; previous and next definitions.
To see what comes back from the C compilation process, we read lines
and search for various patterns. The procedure patMatch takes a
pattern pat (which is just a string, nothing fancy here), and the
subject string target, and returns true if target contains
pat starting at the first character.
<pattern matching procedure definition>= let patMatch = proc(pat,target:string->bool) begin let patlen = length(pat) let targlen := length(target) (if targlen<patlen then false else pat=target(1|patlen)) end
Used above.
To compile the C code, we use gnumake. This is done by writing
the environment variable CPAMMAKE into the socket, which is
expanded into the full make and makefile name to be used. This gives a
measure of independence of this code from the environment, and allows
the C compiler invocation to be changed without having to recompiler
the Napier compiler (this compiler).
<doCompile definition>+=
let makePath := "$CPAMMAKE "
writeString(makePath ++ rawname ++ "'n")
writeString("echo '""++endmessage++"'"'n")
let theend := false
Used above; previous and next definitions.
Read the lines coming back from the shell subprocess. These are
scanned to search for two things:
\begin{enumerate}\itemskip=0pt
\item If the line contains the string
gnumake: *** [pm.raw] Error 1,
then an error occurred in the C compilation.
\item If the line is equal to the string endmessage, then this is
the last line of the script, so we can disconnect the subprocess.
\end{enumerate}
<doCompile definition>+=
while ~(theend or endOfInput()) do begin
let line = readLine()
ws(line++"'n")
if patMatch(line,"gnumake: *** [pm.raw] Error 1")
do compileError := true ! this is a kludge ************
theend := patMatch(line,endmessage)
end
end
Used above; previous and next definitions.
return whether we were successful in compiling this code
<doCompile definition>+= compileError end
Used above; previous definition.
<constructCode definition>= constructCode := proc(state : SpecCPamCaseCGState -> CodeObject) begin !*** One location before start of scalars in cvec. Arithmetic !*** at end ensures word break at start of scalars. !*********************************************************************** !*** I'm not sure that this is appropriate in CPamCase 941112:181603 ajh !*********************************************************************** let scalarStart = pLitStart+state(pntrCount)*stSize+(state(cp)-2) div 4 let frameSize = (state(maxMs) + state(maxPs)) div stSize !*** Do the scalar backpatching.
Used above; next definition.
<constructCode definition>+=
let cvec = state(cvec)
let noOfScalars = state(scalarCount)
let scalarPos := scalarStart
!*** The position each scalar WILL be in in the cvec
let svec = vector 0 to noOfScalars of 0
!*** A vector to hold the scalar values, first is dummy
let scalarListSpot := state(scalarList)
!*** if no scalars. A pointer to locations in the list
Used above; previous and next definitions.
<constructCode definition>+=
for i = 1 to noOfScalars do
begin
let scalarInfo := scalarListSpot'cons(hd)
!*** Get the info from the scalar list
svec(i) := scalarInfo(scalar)
!*** Record the scalar in the vector for later
let scalarUse := scalarInfo(codeOffsets)
!*** Get the list of offsets in the code vector
while scalarUse isnt tip do
begin
cvec(scalarUse'cons(hd)) := CPamInst(pam:scalarStart + i)
scalarUse := scalarUse'cons(tl)
end
scalarListSpot := scalarListSpot'cons(tl)
end
Used above; previous and next definitions.
<constructCode definition>+=
!*** C Compile the code vector
writeString("*** about to call the C Compilation ***'n")
let exactCVec = cCompile(state)
writeString("*** C Compilation returns ***'n")
Used above; previous and next definitions.
<constructCode definition>+= !*** Make a pointer vector - first element is a dummy element - !*** required for empty pntr vec. let pvec = vector 0 to state(pntrCount) of PointerType(Null : nil) let pSpot := state(startPList)'cons(tl)
Used above; previous and next definitions.
<constructCode definition>+=
for i = 1 to state(pntrCount) do
begin
pvec(i) := pSpot'cons(hd)
pSpot := pSpot'cons(tl)
end
Used above; previous and next definitions.
Now construct an instance of NCode that holds the constructed C
code vector.
<constructCode definition>+=
let codeType =10 ! this is a C code code vector
let thisNCode = NCode(exactCVec,
SM(getInfo)(state(symbols))(procInfo)'present(envVector),
svec,
pvec,
scalarStart,
frameSize,
codeType)
Used above; previous and next definitions.
pass the new instance of thisNCode:codeObject back as the
result. This will be used by magic.
<constructCode definition>+= magic(thisNCode) end
Used above; previous definition.
readRawCode reads the code generated by the execution of the self
extracting program compiled and run by the doCompile procedure.
We open the relevant code file, create a read environment for it, then
read bytes from it and copy them to a code vector.
<readRawCode definition>=
let readRawCode = proc(rawname : string ->*CPamInst)
begin
<openCode definition>
let ri = openCode(rawname)
let fenv = makeReadEnv(ri)
use fenv with
endOfInput : proc(->bool);
readByte : proc(->int) in
begin
let cursize := 4096
let cv := vector 1 to cursize of 0
let cvpos := 0
<scan input and collect code vector>
<return a vector trimmed down to exact size>
end
end
DefinesreadRawCode(links are to index).Used above.
*
openCode opens the code file as named by the input parameter, and
checks that it is okay.
<openCode definition>=
let openCode = proc(filename : string -> file)
begin
let file_error = proc(s,s1 : string); begin
writeString("'nCannot " ++ s ++ " file " ++ s1 ++ "'n")
abort()
end
let check_open = proc(F : file; s : string -> file); begin
if F = nilfile do file_error("open",s)
F
end
check_open(open(filename,0),filename)
end
DefinesopenCode(links are to index).Used above.
* Collecting the code from the read environment is straightforward, just read bytes and store them in the code vector. The only complication arises if the code vector is not long enough. In that case, we double the size, copy across the part read so far, and continue.
<scan input and collect code vector>=
while ~ endOfInput() do begin
cvpos := cvpos+1
if cvpos > cursize do begin
cursize := 2*cursize
cv := vector 1 to cursize using
proc(n:int->int); if n<cvpos then cv(n) else 0
end
cv(cvpos) := readByte()
end
Used above.
* Having read all the bytes, we now want to return a code vector that is exactly long enough, and with the values cast into CPam instructions.
<return a vector trimmed down to exact size>=
vector 1 to cvpos using
proc(n:int->CPamInst); CPamInst(pam:cv(n))
Used above.
exportAny.N for the CPamCASE compiler was first created
on 1 Aug 1994 by Quintin Cutts. It was modified for this noweb
document on 22 Aug 1995, by John Hurst.
The purpose of exportAny is to flatten an object contained in an
any into a file.
<exportAny.N>=
! Dependencies : -t boot
project PS() as root onto env :
use root with Error,Library : env in
use Library with
Arithmetical,IO,String,Vector : env ;
getImplementation : proc( string -> env ) in
use Arithmetical with
bitwiseAnd,bitwiseOr,shiftLeft,shiftRight : proc( int,int -> int);
maxint : int;
epsilon,maxreal,pi : real in
use IO with PrimitiveIO : env in
use PrimitiveIO with close : proc( file -> int ) ;
create : proc( string,int -> file ) ;
seek : proc( file,int,int -> int ) ;
setByte : proc( int,int,int -> int ) ;
writeBytes : proc( file,*int,int,int -> int ) in
use String with asciiToString : proc( int -> string );
length : proc( string -> int );
stringToAscii : proc( string -> int ) in
use Vector with lwb,upb : proc[t]( *t -> int ) in
use getImplementation( "wizard" ) with CompilerImplementation : env in
use CompilerImplementation with Passes : env in
use Passes with CPamCaseCG : env in
use CPamCaseCG with Magic : env in
use Magic with exportAny : proc( string,any ) in
begin
let exportError = proc( filename : string ; data : any ; s : string )
use Error with System : env in
use System with exportAny : proc( string,any,string ) in
exportAny( filename,data,s )
<exportAny type checking>
<exportAny versions>
<exportAny constants>
<exportAny Code Planting>
end
default : {}
?
This code is written to a file (or else not used).
<exportAny type checking>=
let VOIDproc =
begin
let PROC = splitAny( any( proc() ; {} ) )( tr )
let eqTypeRep = getEqTypeRep()
proc( tr : TypeRep -> bool )
eqTypeRep( PROC,tr )
end
Used above.
<exportAny versions>=
let magicNo =
begin
let p1 = shiftLeft( 15,28 )
let p2 = shiftLeft( 5,24 )
let p3 = shiftLeft( 12,20 )
let p4 = shiftLeft( 1,16 )
let top = bitwiseOr( p1,p2 + p3 + p4 )
top + 9 !*** Version 9 for PamCase
end
let npcMagic = 6
Used above.
<exportAny constants>= let lower24 = shiftLeft( 1,24 ) - 1 !*** Bits 0-23. let headerBits = shiftLeft( 144,24 ) !*** Bin - 10010000 << 24 places.
Used above.
<exportAny Code Planting>=
exportAny := proc( filename : string ; dataToDump : any )
begin
<exportAny Code Planting: Global Vars>
<exportAny Code Planting: File Handling>
<exportAny Code Planting: Output Procs>
<exportAny Code Planting: Remember where objects are>
<exportAny Code Planting: The generic dump Object>
<exportAny Code Planting: Put out objects in root object>
<exportAny Code Planting: Root Object Planting>
<exportAny Code Planting: Plant data placing pointers in correct place>
<exportAny Code Planting: The Main Body>
end
Used above.
<exportAny Code Planting: Global Vars>= let nilAddr := 0 let nullstrAddr := 0 let nullfileAddr := 0 let codeSize := 0 let noObjects := 0 let stdOut := nilfile
Used above.
<exportAny Code Planting: File Handling>=
let CreateCode = proc( filename : string -> file )
begin
let F = create( filename,493 )
if F = nilfile do exportError( filename,dataToDump,"Cannot create file" )
F
end
Used above.
<exportAny Code Planting: Output Procs>=
let boutl =
begin
let buff := vector 0 to 1 of 0
proc( n : int )
begin
buff( 0 ) := n
let res := writeBytes( stdOut,buff,0,4 )
end
end
let boutd = proc( r : real )
begin
let halfs = fiddleR( r )
for i = lwb[ int ]( halfs ) to upb[ int ]( halfs ) do boutl( halfs( i ) )
end
let outputStInfo = proc( rootAddr : int )
begin
let s = seek( stdOut,0,0 )
boutl( magicNo )
boutl( codeSize )
boutl( noObjects )
boutl( rootAddr )
boutl( npcMagic )
end
let heapHeader = proc( noPntrs,objLength : int -> int )
!*** Put out the header for an object, returns address of start of header.
begin
noObjects := noObjects + 1
boutl( 0 ) !*** Fred's prefix word - poms black magic hex.
let saveAddr = codeSize + 4 !*** This is really the start - honest.
codeSize := codeSize + ( objLength + 1 ) * 4
!*** size + object size + poms black magic hex.
boutl( bitwiseOr( headerBits,noPntrs ) ) !*** noPntrs and mark bits.
boutl( objLength ) !*** Output object length.
saveAddr
end
let Seek = proc( f : file ; index,key : int )
!*** Seek relative to end of file header.
{ let t = seek( f,index + 20,key ) }
let outputObj = proc ( nopntrs,size : int -> int )
begin
let pid = heapHeader(nopntrs,size) !*** Header etc. put out by heapHeader.
for i = 1 to nopntrs do
!*** Some space holding stuff - filled in later.
boutl( nilAddr ) !*** nil for the pointers.
for i = nopntrs + 1 to size - 2 do
!*** Some space holding stuff - filled in later.
boutl( 0 ) !*** zero for the scalars.
pid
end
let WriteIntAt = proc( pid,index,theint : int )
begin
let filePosn = codeSize !*** Save where we are in the file.
Seek( stdOut,pid + 4 * index,0 ) !*** Seek to the new position.
boutl( theint ) !*** Write out the int.
Seek( stdOut,filePosn,0 ) !*** Back to where we were in the file.
end
let WriteBoolAt = proc( pid,index : int ; thebool : bool )
begin
let filePosn = codeSize !*** Save where we are in the file.
Seek( stdOut,pid + 4 * index,0 ) !*** Seek to the new position.
if thebool then boutl( 1 ) !*** Write out the int.
else boutl( 0 )
Seek( stdOut,filePosn,0 ) !*** Back to where we were in the file.
end
Used above.
<exportAny Code Planting: Remember where objects are>=
rec type list is
variant( more : nullToAddr ; tip : null ) &
nullToAddr is structure( pointer : null ; address : int ; next : list )
let remembered := list( tip : nil )
let checkAlready = proc( pointer : null -> int )
begin
let l := remembered
let addr := -1
while l is more and addr = -1 do
begin
let next = l'more
if next( pointer ) = pointer do addr := next( address )
l := next( next )
end
addr
end
let Remember = proc( pointer : null ; address : int )
remembered := list( more : nullToAddr( pointer,address,remembered ) )
Used above.
<exportAny Code Planting: The generic dump Object>=
rec let dumpObject = proc( pointer : null -> int )
!*** Dumps the object returns the file address.
begin
let checkit = checkAlready( pointer )
if checkit ~= -1 then checkit
else
begin
let size = lookupInt( pointer,1 ) !*** Get the size of the object.
let numPntrs = bitwiseAnd( lookupInt( pointer,0 ),lower24 )
let addr = outputObj( numPntrs,size )
Remember( pointer,addr )
for i = 1 to numPntrs do
WriteIntAt( addr,i + 1,dumpObject( lookupPntr( pointer,i + 1 ) ) )
for i = numPntrs + 1 to size - 2 do
WriteIntAt( addr,i + 1,lookupInt( pointer,i + 1 ) )
addr
end
end
Used above.
<exportAny Code Planting: Put out objects in root object>=
let outputNil = proc() !*** Put out nil.
begin
nilAddr := heapHeader( 0,2 )
Remember( nil,nilAddr )
end
let outputNullstr = proc() !*** The value nullstring.
begin
let struc = splitAny( any( "" ) )
nullstrAddr := dumpObject( struc( pointer ) )
end
let outputNullfile = proc() !*** The nilfile value.
begin
let struc = splitAny( any( nilfile ) )
nullfileAddr := dumpObject( struc( pointer ) )
end
let outputNullImage = proc( -> int ) !*** The nilimage value.
begin
let struc = splitAny( any( nilimage ) )
dumpObject( struc( pointer ) )
end
let outputCharsVector = proc( -> int )
begin
let charVec = vector 0 to 127 of ""
for i = 0 to 127 do charVec( i ) := asciiToString( i )
!*** Load the single characters.
let struc = splitAny( any( charVec ) )
dumpObject( struc( pointer ) ) !*** Plant the string vector.
end
let outputFileVec = proc( -> int )
begin
let ofv = vector 1 to 64 of nilfile
let struc = splitAny( any( ofv ) )
let saveAddr = dumpObject( struc( pointer ) )
saveAddr
end
Used above.
<exportAny Code Planting: Root Object Planting>=
!** This needs updating eventually to be consistent with release 2.0 root objects.
!** However, it is ok for the moment. Quintin 26/10/94
let outputRootObj = proc( -> int )
begin
let chVec = outputCharsVector() !*** The single characters.
let nullimageAddr = outputNullImage() !*** The value nullimage.
let fileVec = outputFileVec() !*** The open file descriptor vector.
let thisAddress = heapHeader( 22,34 ) !*** Root object header.
boutl( nilAddr ) !*** The value nil - should be the TYPE !
boutl( nilAddr ) !*** Startup procedure pointer env.
boutl( nilAddr ) !*** Startup procedure scalar env.
boutl( nilAddr ) !*** Persistent root.
boutl( nullfileAddr ) !*** nullfile.
boutl( nullstrAddr ) !*** nilstring.
boutl( chVec ) !*** Characters vector.
boutl( nullimageAddr ) !*** nullfile.
boutl( nilAddr ) !*** Code vector for the error procedure.
boutl( nilAddr ) !*** Static link for the error procedure.
boutl( nilAddr ) !*** Event handling procedures.
boutl( nilAddr ) !*** Error handling procedures.
boutl( fileVec ) !*** Vector of open files.
boutl( nilAddr ) !*** Code vector for type checking procedure.
boutl( nilAddr ) !*** Static link for type checking procedure.
boutl( nilAddr ) !*** Types module.
boutl( nilAddr ) !*** Variant checking workspace.
boutl( nilAddr ) !*** Variant checking workspace.
boutl( nilAddr ) !*** Variant checking workspace.
boutl( nilAddr ) !*** Variant checking workspace.
boutl( nilAddr ) !*** Pointer to currently executing thread's throb.
boutl( nilAddr ) !*** Pointer to list of throbs.
boutl( 0 ) !*** nextThreadId.
boutl( 0 ) !*** Process lockword.
boutl( maxint ) !*** maxint.
boutd( maxreal ) !*** maxreal.
boutd( pi ) !*** pi.
boutd( epsilon ) !*** epsilon.
boutl( npcMagic ) !*** Magic number for saving the stable store.
thisAddress
end
Used above.
<exportAny Code Planting: Plant data placing pointers in correct place>=
let outputData = proc( rootAddr : int )
begin
!*** Split up the data into a null, typeRep & 2 ints.
let struc = splitAny( dataToDump )
if VOIDproc( struc( tr ) ) then !*** Is the data actually a program?
begin
let wrapper = struc( pointer ) !*** Dump the code and static link.
let cvecAddr = dumpObject( lookupPntr( wrapper,2 ) )
let slinkAddr = dumpObject( lookupPntr( wrapper,3 ) )
!*** Setup the root object to look like a program.
WriteIntAt( rootAddr,3,cvecAddr )
WriteIntAt( rootAddr,4,slinkAddr )
end
else
begin
!*** Make a wrapper to hold the any.
let wrapper = makeObject( 6,2 )
!*** Insert the any's components into the wrapper.
assignPntr( wrapper,2,struc( pointer ) )
let trep = splitAny( any( struc( tr ) ) )( pointer )
assignPntr( wrapper,3,trep )
assignInt( wrapper,4,struc( branch ) )
assignInt( wrapper,5,struc( padding ) )
!*** Put the wrapper in the root object's data field.
let dataAddr = dumpObject( wrapper )
WriteIntAt( rootAddr,5,dataAddr )
end
end
Used above.
<exportAny Code Planting: The Main Body>=
stdOut := CreateCode( filename )
if stdOut ~= nilfile do
begin
for i = 1 to 5 do boutl( 0 ) !*** Make space for header.
codeSize := 0 !*** Reset codeSize.
outputNil() !*** Put out nil.
outputNullstr() !*** The value nullstring.
outputNullfile() !*** The value nullfile.
let rootAddr = outputRootObj() !*** outputRootObject information.
outputData( rootAddr ) !*** Plant the pointers to the data.
outputStInfo( rootAddr ) !*** Put out std info.
let throw := close( stdOut )
end
Used above.
magic.N for the CPamCASE compiler was first created
on 26 Oct 1994 by John Hurst from an equivalent version for the
IntPamCASE compiler by Quintin Cutts. It was modified for this noweb
document on 22 Aug 1995, also by John Hurst.
magic turns a description (NCode) of an executable code
object into a valid executable code vector.
<magic.N>=
! Dependencies : -t boot -t cPamCaseCGPass
! Modifications:
! 941030 ajh write code type flag into code object (requires
! NCode to have field `codeType')
project PS() as root onto env :
use root with Error,Library : env in
use Library with getImplementation : proc( string -> env ) in
use getImplementation( "wizard" ) with CompilerImplementation : env in
use CompilerImplementation with Passes,Errors : env in
use Errors with Compilation : env in
use Compilation with Error : proc( string ) in
use Passes with CPamCaseCG : env in
use CPamCaseCG with CGConstants, Magic : env in
use CGConstants with cvecPntrOverhead : int in
use Magic with magic : proc( NCode -> CodeObject ) in
<magic definition>
default : {}
?
This code is written to a file (or else not used).
magic: Construct the code vector object
<magic definition>=
magic := proc(compilerNCode : NCode -> CodeObject)
begin
<magic: Calculate the size of the cv object>
<magic: Create the new object>
<magic: Copy in the pointer literals>
<magic: Copy the ncode code vector into the real code vector>
!** Pad the final word if required (not in CPamCase)
<magic: Copy in the scalar literals>
<magic: Copy in the code flag and frame size>
!****** Make a 'null' view of the env vector
let envAsNull = splitAny( any( compilerNCode( envVec ) ) )( pointer )
!****** Return the null code vector and env template
CodeObject( cvec,envAsNull )
end
Used above.
<magic: Calculate the size of the cv object>= let pointers = compilerNCode(pntrVec) let noOfPntrs = upb[PointerType](pointers) !** start at zero - which is a dummy element let scalars = compilerNCode(scalarVec) let noOfScalars = upb[int](scalars) !** ditto let cCVec = compilerNCode(codeVec) let noOfCVecInts = upb[CPamInst](cCVec) let noOfCVecWords = (noOfCVecInts) !* used to have byte count expr let cvSize = 2 + !* Header and size cvecPntrOverhead + !* Alt cvec, source and spare pntr noOfPntrs + noOfCVecWords + !* No of cvec words using unsigned noOfScalars + !* ints for instrs and parameters 2 !* Code Flag and Frame size
Used below.
<magic: Create the new object>= let cvec = makeObject( cvSize,noOfPntrs + cvecPntrOverhead ) for i = 2 to 4 do assignPntr( cvec,i,nil )
Used below.
Before the pointer literals are copied, they must first be converted into nulls
<magic: Copy in the pointer literals>=
let codeStart = 2+cvecPntrOverhead+noOfPntrs
for i = 2+cvecPntrOverhead to codeStart-1 do
project pointers(i-cvecPntrOverhead-1) as thePntr onto
Null : assignPntr(cvec,i,thePntr)
String : assignString(cvec,i,thePntr)
TypeRep : assignPntr(cvec,i,splitAny(any(thePntr))(pointer))
default : Error( "impossible branch in a PointerType in magic" )
Used below.
<magic: Copy the ncode code vector into the real code vector>=
let nextCVecWord := 0
for i = 1 to noOfCVecInts do
begin
nextCVecWord := bitwiseOr(shiftLeft(nextCVecWord,8),cCVec(i)'pam)
if i rem 4 = 0 do
begin
assignInt( cvec,codeStart - 1 + i div 4,nextCVecWord )
nextCVecWord := 0
end
end
Used below.
<magic: Copy in the scalar literals>= !** CPamCase should have none ... let scalarBase = codeStart + noOfCVecWords for i = scalarBase to scalarBase + noOfScalars - 1 do assignInt( cvec,i,scalars( i - scalarBase + 1 ) )
Used below.
<magic: Copy in the code flag and frame size>= assignInt(cvec, scalarBase+noOfScalars, compilerNCode(codeType)) assignInt(cvec, scalarBase+noOfScalars+1, compilerNCode(frameSize))
Used below.