Citazione da: cdimauro - 20 Gennaio 2014, 07:00:59Ehm, quale sarebbe? Non me lo ricordo adesso.Per come e' fatto il datapath esiste un effetto collaterale della BRA che può essere abilitato (specificando un registro destinazione) e nel caso fa le seguenti faccende1) copia PC+sizeof(instruction) nel registro destinazione2) copia quanto specificato come sorgente (una costante immediata, o il contenuto di un registro) in PCin pratica si comporta come una jsr ma senza toccare lo stack, difatti e' possibile ripristinare all'indietro il PC con la stessa BRA specificando nessun registro destinazione e per sorgente lo stesso registro usato per salvare PC+sizeof(instruction)
Ehm, quale sarebbe? Non me lo ricordo adesso.
Citazione da: cdimauro - 20 Gennaio 2014, 07:00:59Però quelle ALU addizionali sono comode per migliorare le prestazioni. Indubbiamente, tuttavia richiedono una estensione di ciclo macchina per dare il tempo ad una rete di calcolo a due tre livelli di elaborare l'EA propagandone il segnale in modo sincrono nell'apposito registro di datapath, il che quindi mi scoccia un po' perché vorrei uniformita' di ciclo - idealmente - per tutte le istruzioni.
Però quelle ALU addizionali sono comode per migliorare le prestazioni.
Citazione da: TheKaneB - 20 Gennaio 2014, 12:14:59In parte sì, ma io intendo una serializzazione vera e propria di tutto l'albero, cioè la messa su file dell'intera struttura dati invariata. Probabilmente andando nel pratico conviene di più quel metodo, visto che tutti i maggiori compilatori si sono stabilizzati su questo metodo.Probabilmente è più semplice da elaborare,. Cmq, nel caso dell'IR di LLVM, esiste sia in forma testuale (utile da leggere per le persone), sia in forma binaria (memorizzata o su file o in memoria). La documentazione dice che il formato binario è simile a un XML (quindi una albero). Il formato testuale invece è così:Codice: [Seleziona]; ModuleID = 'base.c'target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"; Function Attrs: nounwind uwtabledefine i32 @foo(i32 %a, i32 %b) #0 { %1 = alloca i32, align 4 %2 = alloca i32, align 4 store i32 %a, i32* %1, align 4 store i32 %b, i32* %2, align 4 br label %3; <label>:3 ; preds = %6, %0 %4 = load i32* %1, align 4 %5 = icmp sgt i32 %4, 0 br i1 %5, label %6, label %11; <label>:6 ; preds = %3 %7 = load i32* %2, align 4 %8 = mul nsw i32 %7, 2 %9 = load i32* %1, align 4 %10 = sub nsw i32 %8, %9 store i32 %10, i32* %2, align 4 br label %3; <label>:11 ; preds = %3 %12 = load i32* %2, align 4 ret i32 %12}; Function Attrs: nounwind uwtabledefine i32 @main() #0 { %1 = alloca i32, align 4 %a = alloca i32, align 4 store i32 0, i32* %1 %2 = call i32 @foo(i32 3, i32 4) store i32 %2, i32* %a, align 4 %3 = load i32* %a, align 4 ret i32 %3}attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }!llvm.ident = !{!0}!0 = metadata !{metadata !"Ubuntu clang version 3.4-1~exp1 (trunk) (based on LLVM 3.4)"}Generato compilando senza nessuna ottimizzazione (-O0) a partire da questo programmino inutile:Codice: [Seleziona]int foo(int a, int b){ while(a>0) b = b*2 - a; return b;}int main(){ int a = foo(3,4); return a;}È in formato SSA, quindi con infinite variabili temporanee %xxx (xxx = numero), e come si vede ogni volta che una variabile è usata viene anche specificata la dimensione dell'operando.
In parte sì, ma io intendo una serializzazione vera e propria di tutto l'albero, cioè la messa su file dell'intera struttura dati invariata. Probabilmente andando nel pratico conviene di più quel metodo, visto che tutti i maggiori compilatori si sono stabilizzati su questo metodo.
; ModuleID = 'base.c'target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"; Function Attrs: nounwind uwtabledefine i32 @foo(i32 %a, i32 %b) #0 { %1 = alloca i32, align 4 %2 = alloca i32, align 4 store i32 %a, i32* %1, align 4 store i32 %b, i32* %2, align 4 br label %3; <label>:3 ; preds = %6, %0 %4 = load i32* %1, align 4 %5 = icmp sgt i32 %4, 0 br i1 %5, label %6, label %11; <label>:6 ; preds = %3 %7 = load i32* %2, align 4 %8 = mul nsw i32 %7, 2 %9 = load i32* %1, align 4 %10 = sub nsw i32 %8, %9 store i32 %10, i32* %2, align 4 br label %3; <label>:11 ; preds = %3 %12 = load i32* %2, align 4 ret i32 %12}; Function Attrs: nounwind uwtabledefine i32 @main() #0 { %1 = alloca i32, align 4 %a = alloca i32, align 4 store i32 0, i32* %1 %2 = call i32 @foo(i32 3, i32 4) store i32 %2, i32* %a, align 4 %3 = load i32* %a, align 4 ret i32 %3}attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }!llvm.ident = !{!0}!0 = metadata !{metadata !"Ubuntu clang version 3.4-1~exp1 (trunk) (based on LLVM 3.4)"}
int foo(int a, int b){ while(a>0) b = b*2 - a; return b;}int main(){ int a = foo(3,4); return a;}
p.s.vedi perché pepita inizialmente usava BRAM per avere oltre 1024 registri ? Tutti quei modelli di compilatori assumono che l'architettura virtuale abbia infiniti registri, poi fanno seguire una fase di fitting in cui si ripiega sulle risorse finite del target realeio ho attualmente riservato 10 bit, e posso averne anche 12 volendo, quanto aiuta ?
Citazione da: cdimauro - 20 Gennaio 2014, 20:39:42Ho capito, ma in fin dei conto quanto ti costa questa modifica?Costa parecchio, manda fuori di almeno un giro tutta la sincronia del datapath, ovvero costa di almeno 2 stadi in più necessari per propagare ed elaborare l'EA
Ho capito, ma in fin dei conto quanto ti costa questa modifica?
%0 = 3%1 = %0 + 5%2 = %1 / 4%3 = %2 + %0
Sì, ma non è così semplice realizzare un backend. Mi sono smazzato anche il link che mi hai fornito riguardo all'assembler/disassembler, e c'è parecchio lavoro. Oltre al fatto che LLVM in alcune parti è ancora immaturo o incompleto, per cui ti costringe anche a scrivere direttamente codice C++ se vuoi sfruttare (molto) meglio le caratteristiche della tua ISA. Ad esempio è quello che hanno fatto per supportare l'FPU x86 di x86/x64.
Rimane comunque il casino immenso di dover ricorrere a un assemblatore e linker esterno per generare codice.
Altra cosa, purtroppo manca ancora l'architettura 68000. Per cui credo che certe ottimizzazioni non siano state nemmeno prese in considerazione da chi ha sviluppato LLVM. Anche se devo dire che qualcosa per PowerPC c'è, e pure per ARM, per cui magari si può vedere come hanno implementato o risolto certi problemi.
Citazione da: legacy - 20 Gennaio 2014, 18:04:22p.s.vedi perché pepita inizialmente usava BRAM per avere oltre 1024 registri ? Tutti quei modelli di compilatori assumono che l'architettura virtuale abbia infiniti registri, poi fanno seguire una fase di fitting in cui si ripiega sulle risorse finite del target realeio ho attualmente riservato 10 bit, e posso averne anche 12 volendo, quanto aiuta ?Io continuo a credere che 1024 son troppi, di sicuro non per problemi col compilatore (che più registri ha meglio è), ma per problematiche tipo salvataggio del contesto durante context-switch.
In più, è vero che i compilatori lavorano come se avessero un numero infinito di variabili, ma quel che conta è vedere quali sono i valori che rimangono effettivamente nei registri, perchè se abbiamo ad esempio un codice del genere:Codice: [Seleziona]%0 = 3%1 = %0 + 5%2 = %1 / 4%3 = %2 + %0è evidente che quel che ci serviva erano solo 2 registri invece che 4 variabili temporanee SSA!Per esempio, ho compilato con clang un file di un mio videogioco che contiene una funziona assai complessa, con switch -O3, e l'IR generato raggiungeva le 3000 variabili temporanee!Vuol questo dire che quella funzione girerebbe più velocemente su un processore da 3000 registri? Probabilmente no, perchè molti pezzi della funzione sono calcoli a se stanti che generano pochi valori in output (anzi, in questo caso inseriscono dei valori in delle liste).
Bisognerebbe avere un tool che trova il minor numero di registri per avere un'esecuzione ottimale del codice... probabilmente si può hackerare LLVM per ottenere una simile statistica ma non voglio nemmeno pensare la quantità di lavoro (probabilmente il codice si presta già, ma bisognerebbe imparare tutto il sorgente prima di metterci mano, e io ho solo una vita).
Piuttosto userei la BRAM, come ti avevo accennato, per creare più set di registri che l'OS può scambiare con una singola istruzione, per avere context-switch velocissimi.
Citazione da: cdimauro - 20 Gennaio 2014, 20:46:32Sì, ma non è così semplice realizzare un backend. Mi sono smazzato anche il link che mi hai fornito riguardo all'assembler/disassembler, e c'è parecchio lavoro. Oltre al fatto che LLVM in alcune parti è ancora immaturo o incompleto, per cui ti costringe anche a scrivere direttamente codice C++ se vuoi sfruttare (molto) meglio le caratteristiche della tua ISA. Ad esempio è quello che hanno fatto per supportare l'FPU x86 di x86/x64.Mai detto fosse "facile", ma sicuramente è "più facile" rispetto altri sistemi; vuoi parlare di GCC?
LLVM almeno ha TableGen che ti permette di generare gran parte della struttura automaticamente, almeno le parti più "noiose"...Secondo me, più che immaturità, è un po' un'esigenza: credo sia difficile accontentare così tante architetture diverse in un singolo sistema, quindi molte cose le fanno gestire a mano piuttosto che appesantire TableGen.
Citazione da: cdimauro - 20 Gennaio 2014, 20:46:32Rimane comunque il casino immenso di dover ricorrere a un assemblatore e linker esterno per generare codice.Beh se vuoi avere solo un output binario per test e statistiche, lasci stare i linker e crei solo l'assemblatore integrato,che in realtà non è un assemblatore, semplicemente una passata che invece di generare li codice in formato testuale (assembly), lo genera direttamente in binario.Intanto fai questo, che ti porta già a molte cose interessanti.
Citazione da: cdimauro - 20 Gennaio 2014, 20:46:32Altra cosa, purtroppo manca ancora l'architettura 68000. Per cui credo che certe ottimizzazioni non siano state nemmeno prese in considerazione da chi ha sviluppato LLVM. Anche se devo dire che qualcosa per PowerPC c'è, e pure per ARM, per cui magari si può vedere come hanno implementato o risolto certi problemi.C'era un backend di terze parti per 68k, però era sia riferito a una vecchissima versione di LLVM (2.qualcosa), che era ben diversa dalle ultime. Avevo provato ad adattarla in un'ultima versione, ma non c'ero riuscito, avevano cambiato molte cose. Ho provato pure a compilarla nella versione di LLVM che dicevano loro, ma anche li non compilava. Credo che quella patch fosse incompleta e non avesse mai funzionato.
PowerPC dovrebbe essere molto ben supportata, IBM stessa è interessata al progetto. ARM dovrebbe essere sulla stessa strada.
E comunque LLVM ha backend anche per processori ben più limitati del 68k, quindi non credo ti devi preoccupare. Ho visto che alcuni hanno creato un backend per Z80, devo dargli sicuramente un'occhiata!
No, è proprio immaturità. Infatti c'è scritto che estenderanno questa parte per supportare un po' di altre cose e ricorrere il meno possibile al C++ scritto a manina. Anzi, il loro desiderio è di non ricorrervi proprio: avere tutto TableGen-driven...
No, in questo modo i risultati non sarebbero ottimali. Bisogna generare più passate per ottimizzare al meglio gli offset dei salti e dei riferimenti alla memoria.
Il 68K è una brutta bestia (in tutti sensi, sia positivamente sia negativamente), e certe cosucce non le ritrovi nelle altre architetture supportate da LLVM.
Per questo motivo immagino che bisognerebbe cambiare un po' anche l'ottimizzatore, e questo credo che non sia così facile come scrivere "soltanto" il backend...
Citazione da: cdimauro - 20 Gennaio 2014, 20:49:18OK, ma non mi pare molto, visti anche i vantaggi. Quant'è lunga la tua pipeline?Non l'ho implementata, ma e' in fase di bozza, dato che la maggior parte delle istruzioni impiega 5 step, pensavo a 5 stadi, pero' per il calcolo dell'EA complesso ne servono altri 2 per un totale di 7.
OK, ma non mi pare molto, visti anche i vantaggi. Quant'è lunga la tua pipeline?
Citazione da: Z80Fan - 20 Gennaio 2014, 21:36:10Io continuo a credere che 1024 son troppi, di sicuro non per problemi col compilatore (che più registri ha meglio è), ma per problematiche tipo salvataggio del contesto durante context-switch.il push pop automatico e' vietato perche' costa 2 cicli in piu', per cui e' manuale, o meglio opzionale, come lo e' nel MIPS.Quello che posso fare e' usare un indice aggiuntivo per spiazzare al volo quei registri creando un registro speciale di indice per i softregister, in questo modo puoi dividere quei 1024 registri per 32 ottenendo 32 indici di offset, ovvero puoi servire all'istante 32 context switch senza spostare assolutamente nulla, ti basta caricare il taskID nel registro indice dei registri e questo verra' aggiuntoovvero ottieni una cosa del generetypedef struct{uint32_t r[32];} reg_t;reg_t reg[32];uint32_t reg_i;uint32_t taskid;….reg_i = taskid; /* context switch */reg[taskid].r[..] = ….tutto questo in hw, attivabile a piacere, o disattibabile, ma se attivato il context switch costa solo l'indice taskid
Io continuo a credere che 1024 son troppi, di sicuro non per problemi col compilatore (che più registri ha meglio è), ma per problematiche tipo salvataggio del contesto durante context-switch.
essendo stack e memoria esterna alla fpga, volendo posso usare tutta la bram che mi avanza per avere + task servibili in hw, quindi bram per farne registri, attualmente ho 12 bit disponibili quindi 4096 registri massimi (ognuno da 32bit), quindi 128 task servibili ognuno da 32 registri da 32 bit.vanno bene 32 registri fisici per task ?
Citazione da: cdimauro - 20 Gennaio 2014, 21:56:04No, è proprio immaturità. Infatti c'è scritto che estenderanno questa parte per supportare un po' di altre cose e ricorrere il meno possibile al C++ scritto a manina. Anzi, il loro desiderio è di non ricorrervi proprio: avere tutto TableGen-driven...Sarebbe una magnifica cosa! Vedendo l'incredibile sviluppo di questo progetto, probabilmente ci arriveranno in pochi anni.
Citazione da: cdimauro - 20 Gennaio 2014, 21:56:04No, in questo modo i risultati non sarebbero ottimali. Bisogna generare più passate per ottimizzare al meglio gli offset dei salti e dei riferimenti alla memoria.Questo interessa anche a me perchè in un po' tutte le mie ISA metto (se ci stanno) delle istruzioni per salti "veloci" che indicano l'offset in word a cui saltare direttamente nell'opcode (senza un dato immediato insomma);
non credo sia una cosa così stramba che non ci abbiano pensato; probabilmente vengono già eseguite più passate per poter scegliere sempre meglio la miglior istruzione da usare...
Citazione da: cdimauro - 20 Gennaio 2014, 21:56:04Il 68K è una brutta bestia (in tutti sensi, sia positivamente sia negativamente), e certe cosucce non le ritrovi nelle altre architetture supportate da LLVM.Hai qualche esempio?
Citazione da: cdimauro - 20 Gennaio 2014, 21:56:04Per questo motivo immagino che bisognerebbe cambiare un po' anche l'ottimizzatore, e questo credo che non sia così facile come scrivere "soltanto" il backend...Si possono aggiungere passate di ottimizzazione che lavorano però sull'IR; non so invece se ci sono ottimizzazioni sul codice macchina.Intanto comincio a leggere questo link, e se trovo qualcosa di interessante lo riporto qui.http://llvm.org/docs/CodeGenerator.html
@legacy:Esatto, come pensavo io. Bisognerebbe vedere se dare 16 o 32 registri, forse Cesare può darci qualche consiglio...
Beh, sono anni che ci lavorano. E dietro ormai c'è pure Apple...
Non è che non ci abbiano pensato, ma hanno scaricato tutto sull'assemblatore. Di queste cose, infatti, è lui che ne occupa, impiegando più passate per ottimizzare al meglio salti e offset.
Le istruzioni con due operandi in memoria, ad esempio. Nessuna di quelle architetture le supporta; soltanto il 68K. Per cui al 99% non c'avranno nemmeno pensato a come supportarle.
nelle considerazioni fatte poi pensavo a quanto e' fastidioso usare lo stack perché costa cicli asincroni esterni potenzialmente lunghissimi (rispetto ai cicli cpu) a seconda di quanto e' lenta la ram
Citazione da: cdimauro - 21 Gennaio 2014, 07:03:56Le istruzioni con due operandi in memoria, ad esempio. Nessuna di quelle architetture le supporta; soltanto il 68K. Per cui al 99% non c'avranno nemmeno pensato a come supportarle.Bisognerebbe dargli una tale istruzione e vedere come si comporta. Se l'IR ha la possibilità di fare trasferimenti memoria-memoria, allora credo che già la selezione delle istruzioni farà quel lavoro...
void prova(int a[10], int b[10]){ for(int i = 0; i < 10; i++) a[i] += b[i];}
; Function Attrs: nounwind uwtabledefine void @prova(i32* nocapture %a, i32* nocapture readonly %b) #0 { br label %1; <label>:1 ; preds = %1, %0 %indvars.iv = phi i64 [ 0, %0 ], [ %indvars.iv.next, %1 ] %2 = getelementptr inbounds i32* %b, i64 %indvars.iv %3 = load i32* %2, align 4, !tbaa !1 %4 = getelementptr inbounds i32* %a, i64 %indvars.iv %5 = load i32* %4, align 4, !tbaa !1 %6 = add nsw i32 %5, %3 store i32 %6, i32* %4, align 4, !tbaa !1 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 %exitcond = icmp eq i64 %indvars.iv.next, 10 br i1 %exitcond, label %7, label %1; <label>:7 ; preds = %1 ret void}
# *** IR Dump Before Expand ISel Pseudo-instructions ***:# Machine code for function prova: SSAFunction Live Ins: %RDI in %vreg2, %RSI in %vreg3BB#0: derived from LLVM BB %0 Live Ins: %RDI %RSI %vreg3<def> = COPY %RSI; GR64:%vreg3 %vreg2<def> = COPY %RDI; GR64:%vreg2 %vreg5<def> = MOV32r0 %EFLAGS<imp-def,dead>; GR32:%vreg5 %vreg4<def> = SUBREG_TO_REG 0, %vreg5<kill>, 4; GR64:%vreg4 GR32:%vreg5 Successors according to CFG: BB#1BB#1: derived from LLVM BB %1 Predecessors according to CFG: BB#0 BB#1 %vreg0<def> = PHI %vreg4, <BB#0>, %vreg1, <BB#1>; GR64_NOSP:%vreg0 GR64:%vreg4,%vreg1 %vreg6<def> = MOV32rm %vreg3, 4, %vreg0, 0, %noreg; mem:LD4[%scevgep2](tbaa=<badref>) GR32:%vreg6 GR64:%vreg3 GR64_NOSP:%vreg0 ADD32mr %vreg2, 4, %vreg0, 0, %noreg, %vreg6<kill>, %EFLAGS<imp-def,dead>; mem:ST4[%scevgep](tbaa=<badref>) LD4[%scevgep1](tbaa=<badref>) GR64:%vreg2 GR64_NOSP:%vreg0 GR32:%vreg6 %vreg1<def,tied1> = INC64r %vreg0<tied0>, %EFLAGS<imp-def,dead>; GR64:%vreg1 GR64_NOSP:%vreg0 %vreg7<def,tied1> = SUB64ri8 %vreg1<tied0>, 10, %EFLAGS<imp-def>; GR64:%vreg7,%vreg1 JNE_4 <BB#1>, %EFLAGS<imp-use> JMP_4 <BB#2> Successors according to CFG: BB#2(4) BB#1(124)BB#2: derived from LLVM BB %5 Predecessors according to CFG: BB#1 RET# End machine code for function prova.
# *** IR Dump Before Branch Probability Basic Block Placement ***:# Machine code for function prova: Post SSAFunction Live Ins: %RDI in %vreg2, %RSI in %vreg3BB#0: derived from LLVM BB %0 Live Ins: %RDI %RSI %EAX<def> = MOV32r0 %EFLAGS<imp-def,dead>, %RAX<imp-def> Successors according to CFG: BB#1BB#1: derived from LLVM BB %1 Live Ins: %RDI %RSI %RAX Predecessors according to CFG: BB#0 BB#1 %ECX<def> = MOV32rm %RSI, 4, %RAX, 0, %noreg; mem:LD4[%scevgep2](tbaa=<badref>) ADD32mr %RDI, 4, %RAX, 0, %noreg, %ECX<kill>, %EFLAGS<imp-def,dead>; mem:ST4[%scevgep](tbaa=<badref>) LD4[%scevgep1](tbaa=<badref>) %RAX<def,tied1> = INC64r %RAX<kill,tied0>, %EFLAGS<imp-def,dead> CMP64ri8 %RAX, 10, %EFLAGS<imp-def> JNE_4 <BB#1>, %EFLAGS<imp-use,kill> Successors according to CFG: BB#2(4) BB#1(124)BB#2: derived from LLVM BB %5 Predecessors according to CFG: BB#1 RET# End machine code for function prova.
.text .globl prova .align 16, 0x90 .type prova,@functionprova: # @prova .cfi_startproc# BB#0: xorl %eax, %eax .align 16, 0x90.LBB0_1: # =>This Inner Loop Header: Depth=1 movl (%rsi,%rax,4), %ecx addl %ecx, (%rdi,%rax,4) incq %rax cmpq $10, %rax jne .LBB0_1# BB#2: ret.Ltmp0: .size prova, .Ltmp0-prova .cfi_endproc .ident "Ubuntu clang version 3.4-1~exp1 (trunk) (based on LLVM 3.4)" .section ".note.GNU-stack","",@progbits
.file "<stdin>" .text .globl prova .align 16, 0x90 .type prova,@functionprova: # @prova .cfi_startproc# BB#0: xorl %eax, %eax # <MCInst #5306 XOR32rr # <MCOperand Reg:19> # <MCOperand Reg:19> # <MCOperand Reg:19>> .align 16, 0x90.LBB0_1: # =>This Inner Loop Header: Depth=1 movl (%rsi,%rax,4), %ecx # <MCInst #1579 MOV32rm # <MCOperand Reg:22> # <MCOperand Reg:43> # <MCOperand Imm:4> # <MCOperand Reg:35> # <MCOperand Imm:0> # <MCOperand Reg:0>> addl %ecx, (%rdi,%rax,4) # <MCInst #84 ADD32mr # <MCOperand Reg:39> # <MCOperand Imm:4> # <MCOperand Reg:35> # <MCOperand Imm:0> # <MCOperand Reg:0> # <MCOperand Reg:22>> incq %rax # <MCInst #949 INC64r # <MCOperand Reg:35> # <MCOperand Reg:35>> cmpq $10, %rax # <MCInst #577 CMP64ri8 # <MCOperand Reg:35> # <MCOperand Imm:10>> jne .LBB0_1 # <MCInst #1131 JNE_1 # <MCOperand Expr:(.LBB0_1)>># BB#2: ret # <MCInst #2321 RET>.Ltmp0: .size prova, .Ltmp0-prova .cfi_endproc .ident "Ubuntu clang version 3.4-1~exp1 (trunk) (based on LLVM 3.4)" .section ".note.GNU-stack","",@progbits
Citazione da: cdimauro - 20 Gennaio 2014, 20:35:31Credo che i vantaggi siano due: il primo è che più facile realizzare backend per architetture diverse usando un SSA piuttosto che un AST.Il secondo è che l'SSA permette tutta una serie di ottimizzazioni che sono semplici più implementare rispetto all'AST.Un AST può essere SSA-based
Credo che i vantaggi siano due: il primo è che più facile realizzare backend per architetture diverse usando un SSA piuttosto che un AST.Il secondo è che l'SSA permette tutta una serie di ottimizzazioni che sono semplici più implementare rispetto all'AST.
Ho trovato molto comodo ed elegante ottimizzare/modificare grosse quantità di informazioni su una struttura dati ad albero che su una sequenza di istruzioni di codice. Per esempio slinkando un nodo ti porti via o dietro tutto il codice blocco dipendente che è distribuito in tutto il ramo a cui fa riferimento il nodo, mentre su una sequenza la cosa è molto più complicata (o comunque va bene su modifiche molto locali e vicine tra di loro).
Secondo me la generazione di codice intermedio o non deve essere verso la fine, quando il grosso del lavoro è in gran parte finito
Citazione da: cdimauro - 21 Gennaio 2014, 06:58:05Due in più su 5 sono decisamente tanti. Devo supporre che la tua architettura lavori a clock bassi, vista la pipeline così corta.Non e' pensata per implementazioni al Ghz, punto a quanto disponibile per le fpga di fascia bassa, ovvero 50 max 100Mhz, oltre ci sono casini anche con i tools di sviluppo, oltre al fatto che anche il mio LA non segue nulla oltre i 100Mhz.A parte cio' ora e' senza pipeline o meglio ci sono gli stadi della pipeline ma non lavorano in parallelo ma in modo sequenziale, e fatta così impiega mediamente 2 colpi di clock per stadio (necessari per forti constraints di stabilizzazione interni), ovvero 2 x 5 = 10 colpi di clock per tutte le classi di istruzione eccetto le load store che aprono n cicli addizionali in base a quando risponde la controparte in DTACK (e potenzialmente potrebbe anche non rispondere mai tenendo la CPU sospesa … dovrò aggiungere hw di timeout, ovviamente esterno, con propagazione su /bus_err).Cicli assolutamente regolari, e parlamo di una cadrega che con CLK=50Mhz sputa fuori 50Mhz/10 = 5MIPS senza pipeline e senza cache.E' un bidone
Due in più su 5 sono decisamente tanti. Devo supporre che la tua architettura lavori a clock bassi, vista la pipeline così corta.
Altra conquista, oggi ho messo a posto un casino che non permetteva il test and branch, ovvero adesso e' possibile una cosa del generebra.ifeq $1,$2, offset ; if (reg[$1]==reg[$2]) PC+=offset bra.ifeq $1,$2, $3 ; if (reg[$1]==reg[$2]) PC=reg[$3] prima dovevo passare dalla cmpcmp.ifeq $4, $1,$2 ; if (reg[$1]==reg[$2]) reg[$4]=True else reg[$4]=Falsebra $4, offset ; if (reg[$4]==True) PC+=offset bra $4, $3 ; if (reg[$4]==True) PC=reg[$3] Il casino era dato dallo stadio che accede in lettura alla bram registri per produrre {source1,source2,reg_target,EA_in,EA_out,EA_next_fetch}(non tutti usati, dipende da cosa serve alla istruzione specifica, pero' quel modulo produce tutte le informazioni necessarie, tutto in parallelo)due letture richiedevano uno stadio in + e quindi avevo spezzato su due istruzioni, adesso ho risolto quel casino e si risparmia anche un registro