Mercurial > lcfOS
changeset 405:f381cea07fec
merge
author | Windel Bouwman |
---|---|
date | Thu, 19 Feb 2015 14:10:52 +0100 |
parents | a284749c5729 (diff) 742588fb8cd6 (current diff) |
children | b1daa462ee17 |
files | |
diffstat | 163 files changed, 1199 insertions(+), 12503 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/build.xml Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,9 @@ + +<project name="lcfos" default="all"> + <target name="all"> + <build file="user/build.xml" /> + <build file="kernel/build.xml" /> + </target> + +</project> +
--- a/doc/compiler.rst Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +0,0 @@ - - -.. toctree:: - - ir - -Compiler -======== - -This chapter describes the design of the compiler. -The compiler consists a frontend, mid-end and back-end. The frontend deals with -source file parsing and semantics checking. The mid-end performs optimizations. -This is optional. The back-end generates machine code. The front-end produces -intermediate code. This is a simple representation of the source. The back-end -can accept this kind of representation. - -.. graphviz:: - - digraph x { - rankdir="LR" - 1 [label="c3 source file"] - 10 [label="c3 front end" ] - 11 [label="language X front end" ] - 20 [label="mid end" ] - 30 [label="back end for X86" ] - 31 [label="back end for ARM" ] - 40 [label="object file"] - 1 -> 10 - 10 -> 20 [label="IR-code"] - 11 -> 20 [label="IR-code"] - 20 -> 30 [label="IR-code"] - 20 -> 31 [label="IR-code"] - 30 -> 40 - } - - -IR-code -------- - -The intermediate representation (IR) of a program de-couples the front end -from the backend of the compiler. - -See :doc:`ir` for details about all the available instructions. - - -C3 Front-end ------------- - -For the front-end a recursive descent parser is created for the c3 language. -This is a subset of the C language with some additional features. - -.. graphviz:: - - digraph c3 { - rankdir="LR" - 1 [label="source text"] - 10 [label="lexer" ] - 20 [label="parser" ] - 40 [label="code generation"] - 99 [label="IR-code object"] - 1 -> 10 - 10 -> 20 - 20 -> 40 - 40 -> 99 - } - -.. autoclass:: ppci.c3.Lexer - -.. autoclass:: ppci.c3.Parser - -.. autoclass:: ppci.c3.CodeGenerator - -.. autoclass:: ppci.c3.Builder - -Back-end --------- - -The back-end is more complicated. There are several steps to be taken here. - -1. Instruction selection -2. register allocation -3. Peep hole optimization? -4. real code generation - -.. automodule:: ppci.codegen - :members: - -Instruction selection -~~~~~~~~~~~~~~~~~~~~~ - -The instruction selection phase takes care of scheduling and instruction -selection. The output of this phase is a one frame per function with a flat -list of abstract machine instructions. - -// .. autoclass:: ppci.irmach.Frame - -// .. autoclass:: ppci.irmach.AbstractInstruction - -
--- a/doc/ir.rst Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ - - -IR-code -======= - -Front ends generate this IR-code. Backends transform it into machine code. - -The IR-code is implemented in the ir package. - -.. autoclass:: ppci.ir.Module - -.. autoclass:: ppci.ir.Function - -.. autoclass:: ppci.ir.Block - -A block contains a sequence of statements. - -.. autoclass:: ppci.ir.Statement - -.. autoclass:: ppci.ir.Move - -Jump instructions: - -.. autoclass:: ppci.ir.Jump - -.. autoclass:: ppci.ir.CJump - -Statements can contain again expressions. - -.. autoclass:: ppci.ir.Expression - -.. autoclass:: ppci.ir.Const - -.. autoclass:: ppci.ir.Binop - -.. autoclass:: ppci.ir.Call - -.. autoclass:: ppci.ir.Eseq -
--- a/doc/utils.rst Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ - -Utilities -========= - -Hexfile manipulation --------------------- - - -.. autoclass:: utils.HexFile - - ->>> from utils import HexFile ->>> h = HexFile() ->>> h.dump() -Hexfile containing 0 bytes ->>> h.addRegion(0, bytes([1,2,3])) ->>> h -Hexfile containing 3 bytes - - -Yacc ----- - -.. automodule:: yacc - - -Burg ----- - -.. automodule:: pyburg -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/c3/build.xml Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,19 @@ + +<project name="Examples" default="all"> + + <target name="all" depends="burn2"> + </target> + + <property name="src" value="src" /> + <property name="arch" value="arm"/> + + <target name="burn2"> + <assemble source="startup_stm32f4.asm" target="thumb" output="startup.o" /> + <compile target="thumb" sources="burn2.c3" includes="stm32f4xx.c3" output="burn2.o" /> + <link output="burn2.bin" layout="stm32f4.mmap" + target="thumb" + objects="startup.o;burn2.o" /> + </target> + +</project> +
--- a/examples/c3/recipe.yaml Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ - -link: - inputs: - - assemble: - source: startup_stm32f4.asm - machine: thumb - - compile: - sources: [burn2.c3] - includes: [stm32f4xx.c3] - machine: thumb - output: burn.elf2 - output: burn2.bin - layout: - code: 0x08000000 - data: 0x20000000 -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/c3/stm32f4.mmap Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,9 @@ + + +MEMORY flash LOCATION=0x08000000 SIZE=0x10000 { + SECTION(code) +} + +MEMORY ram LOCATION=0x20000000 SIZE=0x10000 { + SECTION(data) +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/qemu_a9_hello/build.xml Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,16 @@ + +<project name="HelloA9" default="hello"> + + <target name="hello"> + <assemble source="startup_a9.asm" target="arm" output="start.o" /> + <compile target="arm" sources='hello.c3' output="hello.o" /> + <link objects="start.o;hello.o" output="hello.o" + target="arm" + layout="qemu.mmap" /> + <objcopy + objectfile="hello.o" + imagename="flash" + output="hello.bin" /> + </target> +</project> +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/qemu_a9_hello/make.sh Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,4 @@ + +../../python/zcc.py recipe recipe.yaml + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/qemu_a9_hello/qemu.mmap Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,9 @@ + +MEMORY flash LOCATION=0x60010000 SIZE=0x10000 { + SECTION(code) +} + +MEMORY ram LOCATION=0x60020000 SIZE=0x10000 { + SECTION(data) +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/qemu_a9_hello/qemutst.sh Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +echo "Trying to run versatilePB board" + +qemu-system-arm -M realview-pb-a8 -m 128M -kernel hello.bin -serial stdio + +
--- a/examples/qemu_a9_hello/recipe.yaml Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ - - -link: - inputs: - - assemble: - source: startup_a9.asm - machine: arm - - compile: - sources: [hello.c3] - includes: [] - machine: arm - layout: - code: 0x60010000 - data: 0x60020000 - output: hello.bin -
--- a/experiments/qemu_vexpress_a9/display.c Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ - - -#define PL110_CR_EN 0x001 -#define PL110_CR_16BPP 0x008 -#define PL110_CR_MONO 0x010 -#define PL110_CR_TFT 0x020 -#define PL110_CR_MONO_8B 0x040 -#define PL110_CR_DUAL_LCD 0x080 -#define PL110_CR_BGR 0x100 -#define PL110_CR_BEBO 0x200 -#define PL110_CR_BEPO 0x400 -#define PL110_CR_PWR 0x800 - - - - -#define PL110_IOBASE 0x10020000 -#define FB_BASE 0x60050000 - - -typedef unsigned int uint32; -typedef unsigned char uint8; -typedef unsigned short uint16; - -typedef struct -{ - uint32 volatile tim0; //0 - uint32 volatile tim1; //4 - uint32 volatile tim2; //8 - uint32 volatile tim3; //c - uint32 volatile upbase; //10 - uint32 volatile lpbase; //14 - uint32 volatile control; //18 -} PL111MMIO; - -void print_uart0(const char *s); - -extern uint16* image_data; -extern int image_width; -extern int image_height; - -void do_display(void) -{ - uint16 volatile *fb; - PL111MMIO *plio; - int x, y; - - plio = (PL111MMIO*)PL110_IOBASE; - - plio->tim0 = 0x3f1f3f9c; - plio->tim1 = 0x080b61df; - plio->upbase = FB_BASE; - - /* 16-bit color */ - plio->control = PL110_CR_EN | (0xC) | PL110_CR_TFT | PL110_CR_PWR; - - fb = (uint16*)FB_BASE; - - for (x = 0; x < (640 * 480) - 10; ++x) - { - fb[x] = 0x1f << (5 + 6) | 0xf << 5; - } - - print_uart0("Cleared disp\n"); - - for (x = 0; x < image_width; x++) - { - for (y = 0; y < image_height; y++) - { - fb[x + 640 * y] = image_data[x + image_width * y]; - } - } - -} -
--- a/experiments/qemu_vexpress_a9/layout.ld Fri Mar 07 17:10:21 2014 +0100 +++ b/experiments/qemu_vexpress_a9/layout.ld Thu Feb 19 14:10:52 2015 +0100 @@ -3,10 +3,14 @@ SECTIONS { - . = 0x60010000; - .startup . : { startup.o(.text) } + . = 0x010000; + .startup . : { startup.o(.init) } .text : {*(.text)} - .data : {*(.data)} + . = ALIGN(0x4000); + .data : { + *(.padata) + *(.data) + } .bss : {*(.bss)} . = . + 0x1000; stack_top = .;
--- a/experiments/qemu_vexpress_a9/main.c Fri Mar 07 17:10:21 2014 +0100 +++ b/experiments/qemu_vexpress_a9/main.c Thu Feb 19 14:10:52 2015 +0100 @@ -1,6 +1,6 @@ -volatile unsigned int * const UART0_DR = (unsigned int *)0x10009000; +volatile unsigned int * const UART0_DR = (unsigned int *)0x109000; void print_uart0(const char *s) @@ -11,12 +11,10 @@ } } -void do_display(void); void start(void) { print_uart0("Hello world\n"); - do_display(); for (;;); }
--- a/experiments/qemu_vexpress_a9/make.sh Fri Mar 07 17:10:21 2014 +0100 +++ b/experiments/qemu_vexpress_a9/make.sh Thu Feb 19 14:10:52 2015 +0100 @@ -4,12 +4,11 @@ TARGET=arm-none-eabi MCPU=arm926ej-s -python make_image.py $TARGET-as -mcpu=$MCPU -g startup.s -o startup.o $TARGET-gcc -c -g -mcpu=$MCPU -marm main.c -nostdlib -o main.o -$TARGET-gcc -c -g -mcpu=$MCPU -marm display.c -nostdlib -o disp.o -$TARGET-gcc -c -g -mcpu=$MCPU -marm image.c -nostdlib -o img.o -$TARGET-ld -g -T layout.ld main.o startup.o disp.o img.o -o test.elf +$TARGET-ld -g -T layout.ld main.o startup.o -o test.elf $TARGET-objcopy -O binary test.elf test.bin +$TARGET-nm test.elf +$TARGET-objdump -x test.elf
--- a/experiments/qemu_vexpress_a9/make_image.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ - -from PyQt5.QtGui import QImage, QGuiApplication, qRed, qBlue, qGreen - -app = QGuiApplication([]) -img = QImage('../../python/ide/icons/hardware.png') -#print(img) - -f = open('image.c', 'w') -print('typedef unsigned short uint16;',file=f) -print('uint16 image_width = {};'.format(img.width()), file=f) -print('uint16 image_height = {};'.format(img.height()), file=f) -print('uint16 image_data[{}] = {{'.format(img.width()*img.height()+1), file=f) -for y in range(img.height()): - for x in range(img.width()): - pix = img.pixel(x, y) - #print(qRed(pix)) - r = qRed(pix) >> 3 - g = qGreen(pix) >> 2 - b = qBlue(pix) >> 3 - u16 = (r << 11) | (g << 6) | b - assert u16 in range(2**16) - print(' {},'.format(hex(u16)),file=f) - -print('0x0};', file=f) -f.close() -
--- a/experiments/qemu_vexpress_a9/run.sh Fri Mar 07 17:10:21 2014 +0100 +++ b/experiments/qemu_vexpress_a9/run.sh Thu Feb 19 14:10:52 2015 +0100 @@ -1,5 +1,5 @@ #!/bin/bash -qemu-system-arm -M vexpress-a9 -m 128M -kernel test.bin \ - -serial file:output.txt +qemu-system-arm -M vexpress-a9 -m 128M -kernel test.bin \ + -serial stdio # -nographic
--- a/experiments/qemu_vexpress_a9/startup.s Fri Mar 07 17:10:21 2014 +0100 +++ b/experiments/qemu_vexpress_a9/startup.s Thu Feb 19 14:10:52 2015 +0100 @@ -1,8 +1,55 @@ +.section .init .global _Reset; _Reset: + +_start: LDR sp, =stack_top + + ldr r2, =0x10009000 + + mov r1, #'1' + str r1, [r2] + +// Load TTBR0 and TTBR1 + ldr r0, =kernel_table0 + mcr p15, 0, r0, c2, c0, 1 + mcr p15, 0, r0, c2, c0, 0 + + // Domain 0: +mov r0, #3 +mcr p15, 0, r0, c3, c0, 0 + + # ; Enable paging: +mrc p15, 0, r0, c1, c0, 0 +orr r0, r0, #1 +mcr p15, 0, r0, c1, c0, 0 + +// Change uart DR pointer (now located at 0x109000: + ldr r2, =0x109000 + mov r1, #'2' + str r1, [r2] + BL start B . +.section .padata + +/* + Maybe the kernel is loaded at 0x60000000, so we need to map that to first + megabyte also... +*/ + +kernel_table0: + .long 0x00000000 + 0x402 // 0x00000000 # ; Identity map first 1 MB + .long 0x10000000 + 0x402 // 0x00100000 # ; second mb mapping to peripherals + + .rept 0x600 - 2 + .long 0x0 + .endr + .long 0x00000402 // 0x60000000 maps 1 MB to first MB + .rept 0x1000 - 0x601 + .long 0x0 + .endr +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/arch/qemu_m3/archmem.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,8 @@ +module archmem; +import io; + +function void init() +{ +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/arch/qemu_m3/startup_m3.asm Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,6 @@ + + +DCD 0x20000678 ; Setup stack pointer +DCD 0x08000009 ; Reset vector, jump to address 8 +B kernel_start ; Branch to main (this is actually in the interrupt vector) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/arch/qemu_vexpress/layout.mmap Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,14 @@ + +MEMORY image LOCATION=0x10000 SIZE=0x10000 { + SECTION(reset) + SECTION(code) + ALIGN(0x4000) + SECTION(mem_tables) + ALIGN(0x4000) + SECTION(ramdisk) +} + +MEMORY ram LOCATION=0x20000 SIZE=0x10000 { + SECTION(data) +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/arch/qemu_vexpress/start.asm Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,94 @@ + +; This file contains the low level assembly code required for interrupt +; handling and virtual memory. + +section reset + +interrupt_vector_table: +ivt_reset: B start ; 0x0 reset +ivt_undef: B undef_handler ; 0x4 undefined instruction +ivt_svc: B undef_handler ; 0x08 Supervisor call +ivt_prefetch: B undef_handler ; 0x0C prefetch abort +ivt_data: B undef_handler ; 0x10 data abort +ivt_hyptrap: B undef_handler ; 0x14 not used +ivt_irq: B undef_handler ; 0x18 IRQ +ivt_fiq: B undef_handler ; 0x18 FIQ + + +start: + +; Setup the memory manager and the stack before entering kernel + +; Setup TTBR1 (translation table base register) + +ldr r0, =kernel_table0 ; pseudo instruction which loads the value of the symbol +; -KERNEL_BASE +mcr p15, 0, r0, c2, c0, 1 ; TTBR1 +mcr p15, 0, r0, c2, c0, 0 ; TTBR0 + +; Prepare the TTBCR (translation table base control register) +mov r0, 0x1 ; TBD: why set this to 1? +mcr p15, 0, r0, c2, c0, 2 + + +; Set domain 0 to manager: +mov r0, 3 +mcr p15, 0, r0, c3, c0, 0 + + +; Enable the VMSA (Virtual memory system architecture): +mrc p15, 0, r0, c1, c0, 0 +; TODO: +mov r1, 0x1 +orr r0, r0, r1 ; TODO: implement orr r0, r0, 1 +mcr p15, 0, r0, c1, c0, 0 + +; Setup stack: +mov sp, 0x30000 +BL kernel_start ; Branch to main (this is actually in the interrupt vector) +local_loop: +B local_loop + + +; Interrupt handlers: + +undef_handler: +B undef_handler + + +; Assembly language helpers: +; Called to identify the proc: +arch_pfr0: +mrc p15, 0, r0, c0, c1, 0 +mov pc, lr + +arch_pfr1: +mrc p15, 0, r0, c0, c1, 1 +mov pc, lr + +arch_mmfr0: +mrc p15, 0, r0, c0, c1, 4 +mov pc, lr + +arch_mpuir: +mrc p15, 0, r0, c0, c0, 4 +mov pc, lr + + +; Memory map tables: + +section mem_tables + +kernel_table0: + dcd 0x00000402 ; Identity map first 1 MB + dcd 0x10000402 ; Map to peripheral space 1 MB + repeat 0x5FE + dcd 0 + endrepeat + + dcd 0x00000402 ; Alias to 0x0 + + repeat 0x9FF + dcd 0 + endrepeat +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/arch/qemu_vexpress/vexpressA9.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,34 @@ +module arch; +import io; + + +function void init() +{ + // putc(65) + io.print2("PFR0 = ", pfr0()); + io.print2("PFR1 = ", pfr1()); + io.print2("MMFR0 = ", mmfr0()); + + // This below is not compatible with all qemu versions: + // io.print2("MPUIR = ", arch.mpuir()); +} + +function void putc(int c) +{ + var int *UART0DR; + UART0DR = cast<int*>(0x109000); // UART0 DR register when remapped at 1MB + *UART0DR = c; +} + +function void halt() +{ + while(true) + { + } +} + +function int pfr0(); +function int pfr1(); +function int mmfr0(); +// function int mpuir(); +
--- a/kernel/arch_arm.c3 Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -module arch; - -function void init() -{ - var int *UART0DR; - UART0DR = cast<int*>(0x10009000); - *UART0DR = 0x65; -} - -function void halt() -{ -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/build.xml Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,35 @@ + +<project name="lcfos-kernel" default="vexpress"> + + <property name="src" value="src" /> + <property name="arch" value="arm" /> + + + <target name="vexpress"> + + <assemble source="arch/qemu_vexpress/start.asm" + target="arm" output="obj/start.o" /> + + <assemble source="ramdisk.asm" + target="arm" output="obj/ramdisk.o" /> + + <compile target="arm" sources='*.c3;arch/qemu_vexpress/vexpressA9.c3' + output="obj/kernel.o" /> + + <!-- <script file="gen_table.py" /> --> + + <link output="obj/kernel.elf" + target="arm" + layout="arch/qemu_vexpress/layout.mmap" + objects="obj/kernel.o;obj/start.o" /> + + <objcopy + objectfile="obj/kernel.elf" + imagename="image" + format="bin" + output="kernel_arm.bin" /> + + </target> + +</project> +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/debug.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,3 @@ + +module debug; +
--- a/kernel/interrupt.c3 Fri Mar 07 17:10:21 2014 +0100 +++ b/kernel/interrupt.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -1,7 +1,7 @@ module interrupt; -func irq_handle() +function void irq_handle() { }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/io.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,52 @@ +module io; +import arch; + +function void println(string txt) +{ + print(txt); + arch.putc(10); // Newline! +} + +function void print(string txt) +{ + var int i; + i = 0; + + while (i < txt->len) + { + arch.putc(cast<int>(txt->txt[i])); + i = i + 1; + } +} + +// Print integer in hexadecimal notation: +function void print_int(int i) +{ + print("0x"); + + // int txt[20]; + var int b; + var int c; + + for (b=28; b >= 0; b = b - 4) + { + c = (i >> b) & 0xF; + if (c < 10) + { + arch.putc( 48 + c ); + } + else + { + arch.putc( 65 - 10 + c ); + } + } + + arch.putc(10); // Newline! +} + +function void print2(string label, int value) +{ + print(label); + print_int(value); +} +
--- a/kernel/kernel.c3 Fri Mar 07 17:10:21 2014 +0100 +++ b/kernel/kernel.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -4,25 +4,34 @@ import process; import scheduler; import arch; +import io; +import debug; + +// Globals: +var process.process_t* init_proc; + // Main entry point of the kernel: function void start() { - var int* UART0DR; - UART0DR = cast<int*>(0x10009000); // UART0 Data register - *UART0DR = 72; + io.println("Welcome to lcfos!"); + arch.init(); process.init(); - //memory:init(); + memory.init(); + + init_proc = process.create(); + // TODO: copy content into process?? - //Process proc = new process:Process(); + io.print2("init address ", cast<int>(init_proc)); //scheduler:queue(proc); - while(true) {} + io.println("Kernel finished"); + panic(); } - +// Called in total stress: function void panic() { arch.halt();
--- a/kernel/make.sh Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -#!/bin/bash - -../python/zcc.py recipe recipe.yaml
--- a/kernel/memory.c3 Fri Mar 07 17:10:21 2014 +0100 +++ b/kernel/memory.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -1,13 +1,36 @@ module memory; -import process; +import arch; +import io; var int ptr; -function u8* Alloc(int size) +// Let the heap grow upwards.. + +function void init() { - ptr = ptr + size; - return cast<u8*>(ptr); + ptr = 0x80000; } +function byte* alloc(int size) +{ + var int ptr2; + ptr2 = ptr; + io.print2("alloc size ", size); + io.print2("alloc address ", ptr); + + // Increment new free point: + ptr = ptr + size; + return ptr2; +} + +function void memcpy(byte* dst, byte* src, int size) +{ + var int i; + for (i = 0; i < size; i = i + 1) + { + *(dst + i) = *(src + i); + } +} +
--- a/kernel/monitor.sh Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -#!/bin/bash - -socat stdio UNIX-CONNECT:vm.sock
--- a/kernel/process.c3 Fri Mar 07 17:10:21 2014 +0100 +++ b/kernel/process.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -7,30 +7,55 @@ type struct { int id; int status; + process_t* next; // For linked list.. } process_t; // Or, use this list structure: // List<process_t> procs; // init is the root of all processes: -var process_t* init_pid; +var process_t* root_process; + var int next_pid; function void init() { next_pid = 0; - init_pid = Create(); + root_process = create(); } /* Create a new process. */ -function process_t* Create() +function process_t* create() { var process_t* p; - //= memory.Alloc(sizeof(process_t)); + + p = cast<process_t*>(memory.alloc(sizeof(process_t))); p->id = next_pid; + p->status = 0; // Ready! + p->next = cast<process_t*>(0); + + // Increment PID: next_pid = next_pid + 1; + + // Store it in the list: + if (root_process == cast<process_t*>(0)) + { + root_process = p; + } + else + { + var process_t* parent; + parent = root_process; + while (parent->next != cast<process_t*>(0)) + { + parent = parent->next; + } + + parent->next = p; + } + return p; }
--- a/kernel/qemutst.sh Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -set -e - -echo "quit" | socat stdio stdio - -echo "Trying to run test on stellaris qemu machine" - -# -S means halt at start: -qemu-system-arm -M vexpress-a9 -m 128M -kernel kernel.bin \ - -monitor unix:vm.sock,server -serial file:output.txt -S -s - -#sleep 1 - -# Send quit to the monitor application -#echo "quit" | socat stdio UNIX-CONNECT:vm.sock - -#echo "Output from terminal:" -#cat output.txt -#echo "" -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/ramdisk.asm Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,5 @@ + +section ramdisk + +ramdisk_begin: +
--- a/kernel/recipe.yaml Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ - -link: - inputs: - - assemble: - source: startup_a9.asm - machine: thumb - - compile: - sources: [memory.c3, kernel.c3, syscall.c3, process.c3, schedule.c3, arch_arm.c3] - includes: [] - machine: thumb - output: kernel.elf2 - layout: - code: 0x10000 - data: 0x20000000 - output: kernel.bin -
--- a/kernel/schedule.c3 Fri Mar 07 17:10:21 2014 +0100 +++ b/kernel/schedule.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -2,13 +2,16 @@ module scheduler; import process; +import arch; var process.process_t *current; -function void executeNext() +function void execute_next() { var process.process_t *old; + old = 0; + if (old != current) { //execute(current);
--- a/kernel/startup_a9.asm Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ - -; DCD 0x20000678 ; Setup stack pointer -DCD 0x06daa0e3 ; mov sp, #0x60 << 8 -DCD 0x60010009 ; Reset vector, jump to address 8 -B kernel_start ; Branch to main (this is actually in the interrupt vector) -
--- a/kernel/startup_m3.asm Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ - - -DCD 0x20000678 ; Setup stack pointer -DCD 0x08000009 ; Reset vector, jump to address 8 -B kernel_start ; Branch to main (this is actually in the interrupt vector) -
--- a/kernel/syscall.c3 Fri Mar 07 17:10:21 2014 +0100 +++ b/kernel/syscall.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -9,6 +9,8 @@ import process; +/* System call numbers: +*/ const int SendMsg = 1; const int ReceiveMsg = 2; const int Reboot = 3; @@ -18,7 +20,7 @@ function void handle_system_call(int callId, int a, int b) { // Main entry, check what to do here - if (callId == 1) + if (callId == SendMsg) { handle_send_msg(); var process.process_t* proc;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make.sh Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,5 @@ +#!/bin/bash + +export PYTHONPATH=../ppci +../ppci/bin/ppci-build.py --log debug build +
--- a/python/asm.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -from ppci.assembler import Assembler - -if __name__ == '__main__': - # When run as main file, try to grab command line arguments: - parser = argparse.ArgumentParser(description="Assembler") - parser.add_argument('sourcefile', type=argparse.FileType('r'), - help='the source file to assemble') - args = parser.parse_args() - a = Assembler() - obj = a.assemble(args.sourcefile.read())
--- a/python/baselex.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ - -import re -from ppci import Token - -def tokenize(tok_spec, txt): - tok_re = '|'.join('(?P<{}>{})'.format(pair[0], pair[1]) for pair in tok_spec) - gettok = re.compile(tok_re).match - func_map = {pair[0]: pair[2] for pair in tok_spec} - - # Parse line: - line = txt - mo = gettok(line) - pos = 0 - while mo: - typ = mo.lastgroup - val = mo.group(typ) - func = func_map[typ] - if func: - typ, val = func(typ, val) - yield Token(typ, val) - pos = mo.end() - mo = gettok(line, pos) - if len(line) != pos: - raise ParserException('Lex fault at {}'.format(line[pos:]))
--- a/python/burg.x Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ - -%tokens ':' ';' '(' ')' ',' template id number '%%' '%terminal' header - -%% - -burgdef: header '%%' directives '%%' rules { self.system.header_lines = $1.val }; - -directives: - | directives directive; - -directive: termdef; - -termdef: '%terminal' termids; - -termids: - | termids termid; - -termid: id { self.system.add_terminal($1.val) }; - -rules: - | rules rule; - -rule: id ':' tree cost template { self.system.add_rule($1.val, $3, $4, $5.val) }; - -cost: number { return $1.val }; - -tree: id { return self.system.tree($1.val) } - | id '(' tree ')' { return self.system.tree($1.val, $3) } - | id '(' tree ',' tree ')' { return self.system.tree($1.val, $3, $5) }; -
--- a/python/hexutil.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -#!/usr/bin/python - -import sys -import argparse -from utils import HexFile - -def hex2int(s): - if s.startswith('0x'): - s = s[2:] - return int(s, 16) - raise ValueError('Hexadecimal value must begin with 0x') - -parser = argparse.ArgumentParser( - description='hexfile manipulation tool by Windel Bouwman') -subparsers = parser.add_subparsers(title='commands', - description='possible commands', dest='command') - -p = subparsers.add_parser('info', help='dump info about hexfile') -p.add_argument('hexfile', type=argparse.FileType('r')) - -p = subparsers.add_parser('new', help='create a hexfile') -p.add_argument('hexfile', type=argparse.FileType('w')) -p.add_argument('address', type=hex2int, help="hex address of the data") -p.add_argument('datafile', type=argparse.FileType('rb'), help='binary file to add') - -p = subparsers.add_parser('merge', help='merge two hexfiles into a third') -p.add_argument('hexfile1', type=argparse.FileType('r'), help="hexfile 1") -p.add_argument('hexfile2', type=argparse.FileType('r'), help="hexfile 2") -p.add_argument('rhexfile', type=argparse.FileType('w'), help="resulting hexfile") - - -def main(args): - if args.command == 'info': - hexfile = HexFile() - hexfile.load(args.hexfile) - print(hexfile) - for region in hexfile.regions: - print(region) - elif args.command == 'new': - hf = HexFile() - data = args.datafile.read() - hf.addRegion(args.address, data) - hf.save(args.hexfile) - elif args.command == 'merge': - hf = HexFile() - hf.load(args.hexfile1) - hf2 = HexFile() - hf2.load(args.hexfile2) - hf.merge(hf2) - hf.save(args.rhexfile) - else: - raise NotImplementedError() - -if __name__ == '__main__': - args = parser.parse_args() - if not args.command: - parser.print_usage() - sys.exit(1) - main(args)
--- a/python/other/diagrameditor.py Fri Mar 07 17:10:21 2014 +0100 +++ b/python/other/diagrameditor.py Thu Feb 19 14:10:52 2015 +0100 @@ -1,11 +1,16 @@ #!/usr/bin/python -from PyQt4.QtGui import * -from PyQt4.QtCore import * -import sys, json, base64 +import sys +import json +import base64 +import os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'ide')) + +from qtwrapper import QtGui, QtCore, QtWidgets, pyqtSignal, get_icon +from qtwrapper import abspath, Qt from diagramitems import Connection, ResizeSelectionHandle, Block, DiagramScene, CodeBlock -from icons import newicon, saveicon, loadicon import diagramitems """ @@ -15,11 +20,14 @@ run with python 3.x as: $ python [thisfile.py] """ -def indent(lines): - return [' ' + line for line in lines] + -class ParameterDialog(QDialog): - def __init__(self, block, parent = None): +def indent(lines): + return [' ' + line for line in lines] + + +class ParameterDialog(QtWidgets.QDialog): + def __init__(self, block, parent = None): super(ParameterDialog, self).__init__(parent) self.block = block self.button = QPushButton('Ok', self) @@ -31,29 +39,35 @@ l.addRow('Code:', self.codeEdit) l.addWidget(self.button) self.button.clicked.connect(self.OK) - def OK(self): + def OK(self): self.block.name = self.nameEdit.text() self.block.code = self.codeEdit.toPlainText() self.close() -class EditorGraphicsView(QGraphicsView): + +class EditorGraphicsView(QtWidgets.QGraphicsView): def __init__(self, parent=None): - QGraphicsView.__init__(self, parent) - self.setDragMode(QGraphicsView.RubberBandDrag) - self.delShort = QShortcut(QKeySequence.Delete, self) + super().__init__(parent) + self.setObjectName('Editor') + self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag) + self.delShort = QtWidgets.QShortcut(QtGui.QKeySequence.Delete, self) self._model = None - self.treeView = QTreeView() + self.treeView = QtWidgets.QTreeView() self.treeView.clicked.connect(self.itemActivated) + def itemActivated(self, idx): b = idx.internalPointer() s = b.scene() s.clearSelection() b.setSelected(True) + def setDiagram(self, d): self.setScene(d) self.delShort.activated.connect(d.deleteItems) + def getModel(self): return self._model + def setModel(self, m): self._model = m if m: @@ -70,15 +84,19 @@ with open(self.model.filename, 'w') as f: f.write(json.dumps(self.model.Dict, indent=2)) def load(self): - filename = QFileDialog.getOpenFileName(self) - if filename: - self.model = loadModel(filename) + filename = QtWidgets.QFileDialog.getOpenFileName(self) + if filename: + self.model = loadModel(filename) + def newModel(self): - self.model = ModelHierarchyModel() + print('NEW') + self.model = ModelHierarchyModel() + def goUp(self): if hasattr(self.diagram, 'containingBlock'): self.diagram = self.diagram.containingBlock.scene() self.zoomAll() + def showCode(self): if self.model: c = self.model.gencode() @@ -101,22 +119,28 @@ exec(codeview.toPlainText(), globs) runButton.clicked.connect(runIt) d.exec_() + def zoomAll(self): """ zoom to fit all items """ rect = self.diagram.itemsBoundingRect() self.fitInView(rect, Qt.KeepAspectRatio) + def wheelEvent(self, event): pos = event.pos() posbefore = self.mapToScene(pos) - degrees = event.delta() / 8.0 + degrees = event.angleDelta().y() / 8.0 sx = (100.0 + degrees) / 100.0 self.scale(sx, sx) event.accept() + def dragEnterEvent(self, event): - if event.mimeData().hasFormat('component/name'): - event.accept() + if event.mimeData().hasFormat('component/name'): + event.accept() + def dragMoveEvent(self, event): - if event.mimeData().hasFormat('component/name'): event.accept() + if event.mimeData().hasFormat('component/name'): + event.accept() + def dropEvent(self, event): if event.mimeData().hasFormat('component/name'): name = bytes(event.mimeData().data('component/name')).decode() @@ -132,29 +156,39 @@ b.setPos(pos) s.addItem(b) -class LibraryModel(QStandardItemModel): - mimeTypes = lambda self: ['component/name'] - def mimeData(self, idxs): - mimedata = QMimeData() - for idx in idxs: - if idx.isValid(): - txt = self.data(idx, Qt.DisplayRole) - mimedata.setData('component/name', txt) - return mimedata + +class LibraryModel(QtGui.QStandardItemModel): + def __init__(self, parent): + super().__init__(parent) + self.setObjectName('Library') -class ModelHierarchyModel(QAbstractItemModel): + mimeTypes = lambda self: ['component/name'] + def mimeData(self, idxs): + mimedata = QtCore.QMimeData() + for idx in idxs: + if idx.isValid(): + txt = self.data(idx, Qt.DisplayRole) + mimedata.setData('component/name', txt) + return mimedata + + +class ModelHierarchyModel(QtCore.QAbstractItemModel): def __init__(self): super(ModelHierarchyModel, self).__init__() self.rootDiagram = DiagramScene() self.rootDiagram.structureChanged.connect(self.handlechange) self.filename = None + def handlechange(self): self.modelReset.emit() + def setDict(self, d): self.rootDiagram.Dict = d self.modelReset.emit() + def getDict(self): return self.rootDiagram.Dict + Dict = property(getDict, setDict) def gencode(self): c = ['def topLevel():'] @@ -163,6 +197,7 @@ c.append('topLevel()') c.append('print("Done")') return c + def index(self, row, column, parent=None): if parent.isValid(): parent = parent.internalPointer().subModel @@ -174,16 +209,18 @@ # TODO: solve this in a better way. block.index = self.createIndex(row, column, block) return block.index + def parent(self, index): if index.isValid(): block = index.internalPointer() if block.scene() == self.rootDiagram: - return QModelIndex() + return QtCore.QModelIndex() else: print(block) outerBlock = block.scene().containingBlock return outerBlock.index print('parent: No valid index') + def data(self, index, role): if index.isValid() and role == Qt.DisplayRole: b = index.internalPointer() @@ -191,6 +228,7 @@ return b.name elif index.column() == 1: return str(type(b)) + def headerData(self, section, orientation, role): if orientation == Qt.Horizontal and role == Qt.DisplayRole: if section == 0: @@ -199,6 +237,7 @@ return "Type" else: return "x" + def rowCount(self, parent): if parent.column() > 0: return 0 @@ -210,32 +249,37 @@ return 0 else: return len(self.rootDiagram.blocks) + def columnCount(self, parent): return 2 -class LibraryWidget(QListView): - def __init__(self): - super(LibraryWidget, self).__init__(None) + +class LibraryWidget(QtWidgets.QListView): + def __init__(self): + super().__init__() + self.setObjectName('LibraryWidget') self.libraryModel = LibraryModel(self) self.libraryModel.setColumnCount(1) # Create an icon with an icon: - pixmap = QPixmap(60, 60) + pixmap = QtGui.QPixmap(60, 60) pixmap.fill() - painter = QPainter(pixmap) + painter = QtGui.QPainter(pixmap) painter.fillRect(10, 10, 40, 40, Qt.blue) painter.setBrush(Qt.yellow) painter.drawEllipse(20, 20, 20, 20) painter.end() # Fill library: for name in ['CodeBlock:codeBlock', 'DiagramBlock:submod', 'Block:blk']: - self.libraryModel.appendRow(QStandardItem(QIcon(pixmap), name)) + self.libraryModel.appendRow(QtGui.QStandardItem(QtGui.QIcon(pixmap), name)) self.setModel(self.libraryModel) self.setViewMode(self.IconMode) self.setDragDropMode(self.DragOnly) + def warning(txt): QMessageBox.warning(None, "Warning", txt) + def loadModel(filename): try: m = ModelHierarchyModel() @@ -250,8 +294,9 @@ except FileNotFoundError: warning('File [{0}] not found'.format(filename)) -class Main(QMainWindow): - def __init__(self): + +class Main(QtWidgets.QMainWindow): + def __init__(self): super(Main, self).__init__(None) self.editor = EditorGraphicsView() self.setCentralWidget(self.editor) @@ -264,30 +309,32 @@ toolbar = self.addToolBar('Tools') toolbar.setObjectName('Tools') def act(name, shortcut, callback, icon=None): - a = QAction(icon, name, self) if icon else QAction(name, self) + a = QtWidgets.QAction(icon, name, self) if icon else QtWidgets.QAction(name, self) a.setShortcuts(shortcut) a.triggered.connect(callback) toolbar.addAction(a) - act('New', QKeySequence.New, self.editor.newModel, buildIcon(newicon)) - act('Save', QKeySequence.Save, self.editor.save, buildIcon(saveicon)) - act('Load', QKeySequence.Open, self.editor.load, buildIcon(loadicon)) - act('Full screen', QKeySequence("F11"), self.toggleFullScreen) - act('Fit in view', QKeySequence("F8"), self.editor.zoomAll) - act('Go up', QKeySequence(Qt.Key_Up), self.editor.goUp) - act('Model code', QKeySequence("F7"), self.editor.showCode) + act('New', QtGui.QKeySequence.New, self.editor.newModel) + act('Save', QtGui.QKeySequence.Save, self.editor.save) + act('Load', QtGui.QKeySequence.Open, self.editor.load) + act('Full screen', QtGui.QKeySequence("F11"), self.toggleFullScreen) + act('Fit in view', QtGui.QKeySequence("F8"), self.editor.zoomAll) + act('Go up', QtGui.QKeySequence(Qt.Key_Up), self.editor.goUp) + act('Model code', QtGui.QKeySequence("F7"), self.editor.showCode) def addDock(name, widget): - dock = QDockWidget(name, self) + dock = QtWidgets.QDockWidget(name, self) dock.setObjectName(name) dock.setWidget(widget) self.addDockWidget(Qt.LeftDockWidgetArea, dock) addDock('Library', LibraryWidget()) addDock('Model tree', self.editor.treeView) - self.settings = QSettings('windelsoft', 'diagrameditor') + self.settings = QtCore.QSettings('windelsoft', 'diagrameditor') self.loadSettings() - def toggleFullScreen(self): - self.setWindowState(self.windowState() ^ Qt.WindowFullScreen) - self.editor.zoomAll() - def loadSettings(self): + + def toggleFullScreen(self): + self.setWindowState(self.windowState() ^ Qt.WindowFullScreen) + self.editor.zoomAll() + + def loadSettings(self): if self.settings.contains('mainwindowstate'): self.restoreState(self.settings.value('mainwindowstate')) if self.settings.contains('mainwindowgeometry'): @@ -295,7 +342,8 @@ if self.settings.contains('openedmodel'): modelfile = self.settings.value('openedmodel') self.editor.model = loadModel(modelfile) - def closeEvent(self, ev): + + def closeEvent(self, ev): self.settings.setValue('mainwindowstate', self.saveState()) self.settings.setValue('mainwindowgeometry', self.saveGeometry()) if self.editor.model and self.editor.model.filename: @@ -306,11 +354,10 @@ ev.accept() if __name__ == '__main__': - if sys.version_info.major != 3: - print('Please use python 3.x') - sys.exit(1) - app = QApplication(sys.argv) - main = Main() - main.show() - app.exec_() - + if sys.version_info.major != 3: + print('Please use python 3.x') + sys.exit(1) + app = QtWidgets.QApplication(sys.argv) + main = Main() + main.show() + app.exec_()
--- a/python/other/diagramitems.py Fri Mar 07 17:10:21 2014 +0100 +++ b/python/other/diagramitems.py Thu Feb 19 14:10:52 2015 +0100 @@ -2,46 +2,62 @@ Contains all blocks that can be used to build models. """ -from PyQt4.QtGui import * -from PyQt4.QtCore import * +import sys +import json +import base64 +import os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'ide')) + +from qtwrapper import QtGui, QtCore, QtWidgets, pyqtSignal, get_icon +from qtwrapper import abspath, Qt + def uniqify(name, names): - newname, i = name, 1 - while newname in names: newname, i = name + str(i), i + 1 - return newname + newname, i = name, 1 + while newname in names: newname, i = name + str(i), i + 1 + return newname + def enum(**enums): - return type('Enum', (), enums) + return type('Enum', (), enums) + Position = enum(TOP=0, TOP_RIGHT=1, RIGHT=2, BOTTOM_RIGHT=3, BOTTOM=4, BOTTOM_LEFT=5, LEFT=6, TOP_LEFT=7) + def buildPath(pts): - path = QPainterPath(pts[0]) - for pt in pts[1:]: path.lineTo(pt) - return path + path = QtGui.QPainterPath(pts[0]) + for pt in pts[1:]: path.lineTo(pt) + return path def equalSpace(n, l, offset=15): - if n == 1: - return [l / 2] - elif n > 1: - return [offset + (l - offset*2)/(n - 1)*i for i in range(n)] - return [] + if n == 1: + return [l / 2] + elif n > 1: + return [offset + (l - offset*2)/(n - 1)*i for i in range(n)] + return [] -class Connection(QGraphicsPathItem): + +class Connection(QtWidgets.QGraphicsPathItem): """ A connection between blocks """ def __init__(self, fromPort=None, toPort=None): - super(Connection, self).__init__() - self.pos2 = self.fromPort = self.toPort = None - self.setFlags(self.ItemIsSelectable | self.ItemClipsToShape) - pen = QPen(Qt.blue, 2, cap=Qt.RoundCap) - self.setPen(pen) - self.arrowhead = QGraphicsPathItem(self) - self.arrowhead.setPath(buildPath([QPointF(0.0, 0.0), QPointF(-6.0, 10.0), QPointF(6.0, 10.0), QPointF(0.0, 0.0)])) - self.arrowhead.setPen(pen) - self.arrowhead.setBrush(QBrush(pen.color())) - self.vias = [] - self.setFromPort(fromPort) - self.setToPort(toPort) + super(Connection, self).__init__() + self.pos2 = self.fromPort = self.toPort = None + self.setFlags(self.ItemIsSelectable | self.ItemClipsToShape) + pen = QtGui.QPen(Qt.blue, 2, cap=Qt.RoundCap) + self.setPen(pen) + self.arrowhead = QtWidgets.QGraphicsPathItem(self) + self.arrowhead.setPath(buildPath([QtCore.QPointF(0.0, 0.0), + QtCore.QPointF(-6.0, 10.0), + QtCore.QPointF(6.0, 10.0), + QtCore.QPointF(0.0, 0.0)])) + self.arrowhead.setPen(pen) + self.arrowhead.setBrush(QtGui.QBrush(pen.color())) + self.vias = [] + self.setFromPort(fromPort) + self.setToPort(toPort) + def getDict(self): d = {} d['fromBlock'] = self.fromPort.block.name @@ -79,14 +95,17 @@ return self.fromPort.scenePos() def setBeginPos(self, pos1): self.updateLineStukken() def setEndPos(self, endpos): - self.pos2 = endpos - self.updateLineStukken() + self.pos2 = endpos + self.updateLineStukken() def itemChange(self, change, value): - if change == self.ItemSelectedHasChanged: - for via in self.vias: - via.setVisible(value) - return super(Connection, self).itemChange(change, value) - def shape(self): return self.myshape + if change == self.ItemSelectedHasChanged: + for via in self.vias: + via.setVisible(value) + return super(Connection, self).itemChange(change, value) + + def shape(self): + return self.myshape + def updateLineStukken(self): """ This algorithm determines the optimal routing of all signals. @@ -97,13 +116,13 @@ if pos1 is None or pos2 is None: return scene = self.scene() - vias = [pos1 + QPointF(20, 0)] + self.vias + [pos2 + QPointF(-20, 0)] + vias = [pos1 + QtCore.QPointF(20, 0)] + self.vias + [pos2 + QtCore.QPointF(-20, 0)] if scene: - litem = QGraphicsLineItem() + litem = QtWidgets.QGraphicsLineItem() litem.setFlags(self.ItemIsSelectable) scene.addItem(litem) for p1, p2 in zip(vias[:-1], vias[1:]): - line = QLineF(p1, p2) + line = QtCore.QLineF(p1, p2) litem.setLine(line) citems = scene.collidingItems(litem) citems = [i for i in citems if type(i) is Block] @@ -115,19 +134,19 @@ self.setPath(p) """ Create a shape outline using the path stroker """ s = super(Connection, self).shape() - pps = QPainterPathStroker() + pps = QtGui.QPainterPathStroker() pps.setWidth(10) self.myshape = pps.createStroke(s).simplified() -class PortItem(QGraphicsPathItem): +class PortItem(QtWidgets.QGraphicsPathItem): """ Represents a port to a subsystem """ def __init__(self, name, block): super(PortItem, self).__init__(block) - self.textItem = QGraphicsTextItem(self) + self.textItem = QtWidgets.QGraphicsTextItem(self) self.connection = None self.block = block - self.setCursor(QCursor(Qt.CrossCursor)) - self.setPen(QPen(Qt.blue, 2, cap=Qt.RoundCap)) + self.setCursor(QtGui.QCursor(Qt.CrossCursor)) + self.setPen(QtGui.QPen(Qt.blue, 2, cap=Qt.RoundCap)) self.name = name self.posCallbacks = [] self.setFlag(self.ItemSendsScenePositionChanges, True) @@ -148,87 +167,101 @@ return value return super(PortItem, self).itemChange(change, value) + class OutputPort(PortItem): - def __init__(self, name, block, d=10.0): - super(OutputPort, self).__init__(name, block) - self.setPath(buildPath([QPointF(0.0, -d), QPointF(d, 0), QPointF(0.0, d)])) - def mousePressEvent(self, event): - self.scene().startConnection(self) + def __init__(self, name, block, d=10.0): + super().__init__(name, block) + self.setPath(buildPath([QtCore.QPointF(0.0, -d), QtCore.QPointF(d, 0), + QtCore.QPointF(0.0, d)])) + + def mousePressEvent(self, event): + self.scene().startConnection(self) + class InputPort(PortItem): - def __init__(self, name, block, d=10.0): - super(InputPort, self).__init__(name, block) - self.setPath(buildPath([QPointF(-d, -d), QPointF(0, 0), QPointF(-d, d)])) + def __init__(self, name, block, d=10.0): + super().__init__(name, block) + self.setPath(buildPath([QtCore.QPointF(-d, -d), QtCore.QPointF(0, 0), + QtCore.QPointF(-d, d)])) -class Handle(QGraphicsEllipseItem): +class Handle(QtWidgets.QGraphicsEllipseItem): """ A handle that can be moved by the mouse """ def __init__(self, dx=10.0, parent=None): - super(Handle, self).__init__(QRectF(-0.5*dx,-0.5*dx,dx,dx), parent) - self.setBrush(QBrush(Qt.white)) + super(Handle, self).__init__(QtCore.QRectF(-0.5*dx,-0.5*dx,dx,dx), parent) + self.setBrush(QtGui.QBrush(Qt.white)) self.setFlags(self.ItemIsMovable) self.setZValue(1) self.setVisible(False) - self.setCursor(QCursor(Qt.SizeFDiagCursor)) + self.setCursor(QtGui.QCursor(Qt.SizeFDiagCursor)) def mouseMoveEvent(self, event): """ Move function without moving the other selected elements """ p = self.mapToParent(event.pos()) self.setPos(p) + class ResizeSelectionHandle(Handle): - def __init__(self, position, block): - super(ResizeSelectionHandle, self).__init__(dx=12, parent=block) - self.position = position - self.block = block - if position in [Position.TOP_LEFT, Position.BOTTOM_RIGHT]: - self.setCursor(QCursor(Qt.SizeFDiagCursor)) - elif position in [Position.TOP_RIGHT, Position.BOTTOM_LEFT]: - self.setCursor(QCursor(Qt.SizeBDiagCursor)) - elif position in [Position.TOP, Position.BOTTOM]: - self.setCursor(QCursor(Qt.SizeVerCursor)) - elif position in [Position.LEFT, Position.RIGHT]: - self.setCursor(QCursor(Qt.SizeHorCursor)) - def mouseMoveEvent(self, event): - self.block.sizerMoveEvent(self, event.scenePos()) + def __init__(self, position, block): + super(ResizeSelectionHandle, self).__init__(dx=12, parent=block) + self.position = position + self.block = block + if position in [Position.TOP_LEFT, Position.BOTTOM_RIGHT]: + self.setCursor(QtGui.QCursor(Qt.SizeFDiagCursor)) + elif position in [Position.TOP_RIGHT, Position.BOTTOM_LEFT]: + self.setCursor(QtGui.QCursor(Qt.SizeBDiagCursor)) + elif position in [Position.TOP, Position.BOTTOM]: + self.setCursor(QtGui.QCursor(Qt.SizeVerCursor)) + elif position in [Position.LEFT, Position.RIGHT]: + self.setCursor(QtGui.QCursor(Qt.SizeHorCursor)) -class Block(QGraphicsRectItem): + def mouseMoveEvent(self, event): + self.block.sizerMoveEvent(self, event.scenePos()) + + +class Block(QtWidgets.QGraphicsRectItem): """ Represents a block in the diagram. """ def __init__(self, name='Untitled', parent=None): super(Block, self).__init__(parent) self.selectionHandles = [ResizeSelectionHandle(i, self) for i in range(8)] # Properties of the rectangle: - self.setPen(QPen(Qt.blue, 2)) - self.setBrush(QBrush(Qt.lightGray)) + self.setPen(QtGui.QPen(Qt.blue, 2)) + self.setBrush(QtGui.QBrush(Qt.lightGray)) self.setFlags(self.ItemIsSelectable | self.ItemIsMovable | self.ItemSendsScenePositionChanges) - self.setCursor(QCursor(Qt.PointingHandCursor)) + self.setCursor(QtGui.QCursor(Qt.PointingHandCursor)) self.setAcceptHoverEvents(True) - self.label = QGraphicsTextItem(name, self) + self.label = QtWidgets.QGraphicsTextItem(name, self) self.name = name # Create corner for resize: - button = QPushButton('+in') + button = QtWidgets.QPushButton('+in') button.clicked.connect(self.newInputPort) - self.buttonItemAddInput = QGraphicsProxyWidget(self) + self.buttonItemAddInput = QtWidgets.QGraphicsProxyWidget(self) self.buttonItemAddInput.setWidget(button) self.buttonItemAddInput.setVisible(False) - button = QPushButton('+out') + button = QtWidgets.QPushButton('+out') button.clicked.connect(self.newOutputPort) - self.buttonItemAddOutput = QGraphicsProxyWidget(self) + self.buttonItemAddOutput = QtWidgets.QGraphicsProxyWidget(self) self.buttonItemAddOutput.setWidget(button) self.buttonItemAddOutput.setVisible(False) # Inputs and outputs of the block: self.inputs = [] self.outputs = [] self.changeSize(2,2) + def editParameters(self): - pd = ParameterDialog(self, self.window()) - pd.exec_() + pd = ParameterDialog(self, self.window()) + pd.exec_() + def newInputPort(self): - names = [i.name for i in self.inputs + self.outputs] - self.addInput(InputPort(uniqify('in', names), self)) + names = [i.name for i in self.inputs + self.outputs] + self.addInput(InputPort(uniqify('in', names), self)) + def newOutputPort(self): - names = [i.name for i in self.inputs + self.outputs] - self.addOutput(OutputPort(uniqify('out', names), self)) + names = [i.name for i in self.inputs + self.outputs] + self.addOutput(OutputPort(uniqify('out', names), self)) + def setName(self, name): self.label.setPlainText(name) + def getName(self): return self.label.toPlainText() + name = property(getName, setName) def getDict(self): d = {'x': self.scenePos().x(), 'y': self.scenePos().y()} @@ -247,14 +280,17 @@ for outp in d['outputs']: self.addOutput(OutputPort(outp['name'], self)) Dict = property(getDict, setDict) + def addInput(self, i): - self.inputs.append(i) - self.updateSize() + self.inputs.append(i) + self.updateSize() + def addOutput(self, o): - self.outputs.append(o) - self.updateSize() + self.outputs.append(o) + self.updateSize() + def contextMenuEvent(self, event): - menu = QMenu() + menu = QtWidgets.QMenu() pa = menu.addAction('Parameters') pa.triggered.connect(self.editParameters) menu.exec_(event.screenPos()) @@ -269,13 +305,13 @@ return super(Block, self).itemChange(change, value) def hoverEnterEvent(self, event): - if not self.isSelected(): - self.repositionAndShowHandles() - super(Block, self).hoverEnterEvent(event) + if not self.isSelected(): + self.repositionAndShowHandles() + super().hoverEnterEvent(event) def hoverLeaveEvent(self, event): - if not self.isSelected(): - [h.setVisible(False) for h in self.selectionHandles] - super(Block, self).hoverLeaveEvent(event) + if not self.isSelected(): + [h.setVisible(False) for h in self.selectionHandles] + super().hoverLeaveEvent(event) def myDelete(self): for p in self.inputs + self.outputs: if p.connection: p.connection.myDelete() @@ -316,31 +352,35 @@ for outp, y in zip(self.outputs, equalSpace(len(self.outputs), h)): outp.setPos(w, y) def setCenterAndSize(self, center, size): - self.changeSize(size.width(), size.height()) - p = QPointF(size.width(), size.height()) - self.setPos(center - p / 2) + self.changeSize(size.width(), size.height()) + p = QtCore.QPointF(size.width(), size.height()) + self.setPos(center - p / 2) def changeSize(self, w, h): - minw = 150 - minh = 50 - h = minh if h < minh else h - w = minw if w < minw else w - self.setRect(0.0, 0.0, w, h) - rect = self.label.boundingRect() - self.label.setPos((w - rect.width()) / 2, (h - rect.height()) / 2) - self.updateSize() + minw = 150 + minh = 50 + h = minh if h < minh else h + w = minw if w < minw else w + self.setRect(0.0, 0.0, w, h) + rect = self.label.boundingRect() + self.label.setPos((w - rect.width()) / 2, (h - rect.height()) / 2) + self.updateSize() + class CodeBlock(Block): - def __init__(self, name='Untitled', parent=None): - super(CodeBlock, self).__init__(name, parent) - self.code = '' - def setDict(self, d): - super(CodeBlock, self).setDict(d) - self.code = d['code'] - def getDict(self): - d = super(CodeBlock, self).getDict() - d['code'] = self.code - return d - def gencode(self): + def __init__(self, name='Untitled', parent=None): + super(CodeBlock, self).__init__(name, parent) + self.code = '' + + def setDict(self, d): + super(CodeBlock, self).setDict(d) + self.code = d['code'] + + def getDict(self): + d = super(CodeBlock, self).getDict() + d['code'] = self.code + return d + + def gencode(self): c = ['def {0}():'.format(self.name)] if self.code: c += indent(self.code.split('\n')) @@ -348,24 +388,28 @@ c += indent(['pass']) return c + class DiagramBlock(Block): - def __init__(self, name='Untitled', parent=None): - super(DiagramBlock, self).__init__(name, parent) - self.subModel = DiagramScene() - self.subModel.containingBlock = self - def setDict(self, d): - self.subModel.Dict = d['submodel'] - def mouseDoubleClickEvent(self, event): - # descent into child diagram - #self.editParameters() - print('descent') - scene = self.scene() - if scene: - for view in scene.views(): - view.diagram = self.subModel - view.zoomAll() + def __init__(self, name='Untitled', parent=None): + super(DiagramBlock, self).__init__(name, parent) + self.subModel = DiagramScene() + self.subModel.containingBlock = self + + def setDict(self, d): + self.subModel.Dict = d['submodel'] -class DiagramScene(QGraphicsScene): + def mouseDoubleClickEvent(self, event): + # descent into child diagram + #self.editParameters() + print('descent') + scene = self.scene() + if scene: + for view in scene.views(): + view.diagram = self.subModel + view.zoomAll() + + +class DiagramScene(QtWidgets.QGraphicsScene): """ A diagram scene consisting of blocks and connections """ structureChanged = pyqtSignal() def __init__(self):
--- a/python/ppci/__init__.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -# File to make this directory a package. - -import sys -import os - -version = '0.0.1' - -# Assert python version: -if sys.version_info.major != 3: - print("Needs to be run in python version 3.x") - sys.exit(1) - -from .common import SourceLocation, SourceRange, Token -from .common import CompilerError, DiagnosticsManager - -logformat='%(asctime)s|%(levelname)s|%(name)s|%(message)s' - -def same_dir(full_path, filename): - return os.path.join(os.path.dirname(os.path.abspath(full_path)), filename)
--- a/python/ppci/assembler.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,204 +0,0 @@ - -import re -import pyyacc -from . import Token, CompilerError, SourceLocation -from .target import Target, Label - - -def bit_type(value): - assert value < (2**32) - assert value >= 0 - t = 'val32' - for n in [16, 12, 8, 5, 3]: - if value < (2**n): - t = 'val{}'.format(n) - return t - -def tokenize(s, kws): - """ - Tokenizer, generates an iterator that - returns tokens! - - This GREAT example was taken from python re doc page! - """ - tok_spec = [ - ('REAL', r'\d+\.\d+'), - ('HEXNUMBER', r'0x[\da-fA-F]+'), - ('NUMBER', r'\d+'), - ('ID', r'[A-Za-z][A-Za-z\d_]*'), - ('SKIP', r'[ \t]'), - ('LEESTEKEN', r':=|[\.,=:\-+*\[\]/\(\)]|>=|<=|<>|>|<|}|{'), - ('STRING', r"'.*?'"), - ('COMMENT', r";.*") - ] - tok_re = '|'.join('(?P<%s>%s)' % pair for pair in tok_spec) - gettok = re.compile(tok_re).match - line = 1 - pos = line_start = 0 - mo = gettok(s) - while mo is not None: - typ = mo.lastgroup - val = mo.group(typ) - if typ == 'NEWLINE': - line_start = pos - line += 1 - elif typ != 'SKIP': - if typ == 'LEESTEKEN': - typ = val - elif typ == 'NUMBER': - val = int(val) - elif typ == 'HEXNUMBER': - val = int(val[2:], 16) - typ = 'NUMBER' - elif typ == 'REAL': - val = float(val) - elif typ == 'STRING': - val = val[1:-1] - elif typ == 'ID': - if val.lower() in kws: # ['r3', 'sp', 'add', 'yield', 'r4', 'r0', 'r1', 'sub', 'r5', 'r6', 'r2']: - typ = val.lower() - col = mo.start() - line_start - loc = SourceLocation('', line, col, 0) # TODO retrieve length? - if typ == 'NUMBER': - typ = bit_type(val) - yield Token(typ, val, loc) - pos = mo.end() - mo = gettok(s, pos) - if pos != len(s): - col = pos - line_start - loc = SourceLocation('', line, col, 0) - raise CompilerError('Unexpected character {0}'.format(s[pos]), loc) - yield Token('EOF', pyyacc.EOF) - - -class Lexer: - def __init__(self, src, kws): - self.tokens = tokenize(src, kws) - self.curTok = self.tokens.__next__() - - def next_token(self): - t = self.curTok - if t.typ != 'EOF': - self.curTok = self.tokens.__next__() - return t - - -class Parser: - def add_rule(self, prod, rhs, f): - """ Helper function to add a rule, why this is required? """ - if prod == 'instruction': - def f_wrap(*args): - i = f(args) - self.emit(i) - else: - def f_wrap(*rhs): - return f(rhs) - self.g.add_production(prod, rhs, f_wrap) - - def __init__(self, kws, instruction_rules, emit): - # Construct a parser given a grammar: - tokens2 = ['ID', 'NUMBER', ',', '[', ']', ':', '+', '-', '*', - pyyacc.EPS, 'COMMENT', '{', '}', - pyyacc.EOF, 'val32', 'val16', 'val12', 'val8', 'val5', 'val3'] - tokens2.extend(kws) - self.kws = kws - g = pyyacc.Grammar(tokens2) - self.g = g - # Global structure of assembly line: - g.add_production('asmline', ['asmline2']) - g.add_production('asmline', ['asmline2', 'COMMENT']) - g.add_production('asmline2', ['label', 'instruction']) - g.add_production('asmline2', ['instruction']) - g.add_production('asmline2', ['label']) - g.add_production('asmline2', []) - g.add_production('label', ['ID', ':'], self.p_label) - #g.add_production('label', []) - - # Add instruction rules for the target in question: - for prod, rhs, f in instruction_rules: - self.add_rule(prod, rhs, f) - - #g.add_production('instruction', []) - g.add_production('expression', ['term'], lambda x: x) - g.add_production('expression', ['expression', 'addop', 'term'], self.p_binop) - g.add_production('addop', ['-'], lambda x: x.val) - g.add_production('addop', ['+'], lambda x: x.val) - g.add_production('mulop', ['*'], lambda x: x.val) - g.add_production('term', ['factor'], lambda x: x) - g.add_production('term', ['term', 'mulop', 'factor'], self.p_binop) - g.add_production('factor', ['ID'], lambda name: ASymbol(name.val)) - g.add_production('factor', ['NUMBER'], lambda num: ANumber(int(num.val))) - g.start_symbol = 'asmline' - self.emit = emit - self.p = g.generate_parser() - # print('length of table:', len(self.p.action_table)) - - # Parser handlers: - def p_ins_1(self, opc, ops): - ins = AInstruction(opc, ops) - self.emit(ins) - - def p_ins_2(self, opc): - self.p_ins_1(opc, []) - - def p_operands_1(self, op1): - return [op1] - - def p_operands_2(self, ops, comma, op2): - assert type(ops) is list - ops.append(op2) - return ops - - def p_listitems_1(self, li1): - return [li1] - - def p_listitems_2(self, lis, comma, li2): - assert type(lis) is list - lis.append(li2) - return lis - - def p_list_op(self, brace_open, lst, brace_close): - return AUnop('{}', lst) - - def p_mem_op(self, brace_open, exp, brace_close): - return AUnop('[]', exp) - - def p_label(self, lname, cn): - lab = Label(lname.val) - self.emit(lab) - - def p_binop(self, exp1, op, exp2): - return ABinop(op, exp1, exp2) - - def parse(self, lexer): - self.p.parse(lexer) - - -class Assembler: - def __init__(self, target): - self.target = target - assert isinstance(target, Target) - self.parser = Parser(target.asm_keywords, target.assembler_rules, self.emit) - - def emit(self, *args): - self.stream.emit(*args) - - # Top level interface: - def parse_line(self, line): - """ Parse line into assembly instructions """ - tokens = Lexer(line, self.target.asm_keywords) - self.parser.parse(tokens) - - def assemble(self, asmsrc, stream): - """ Assemble this source snippet """ - if hasattr(asmsrc, 'read'): - asmsrc2 = asmsrc.read() - asmsrc.close() - asmsrc = asmsrc2 - # TODO: use generic newline?? - # TODO: the bothersome newline ... - self.stream = stream - for line in asmsrc.split('\n'): - self.parse_line(line) - self.stream = None -
--- a/python/ppci/bitfun.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ - - -def rotate_right(v, n): - """ bit-wise Rotate right n times """ - mask = (2**n) - 1 - mask_bits = v & mask - return (v >> n) | (mask_bits << (32 - n)) - -def rotate_left(v, n): - assert n >= 0 - assert n < 32 - return rotate_right(v, 32 - n) - -
--- a/python/ppci/buildtasks.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ - -""" -Defines task classes that can compile, link etc.. -Task can depend upon one another. -""" - -import logging - -from .c3 import Builder -from .irutils import Verifier -from .codegen import CodeGenerator -from .transform import CleanPass, RemoveAddZero -from .tasks import Task, TaskError -from . import DiagnosticsManager, CompilerError -from .assembler import Assembler -from .objectfile import ObjectFile -from .linker import Linker -from .outstream import BinaryOutputStream - - -class BuildTask(Task): - """ Base task for all kind of weird build tasks """ - def __init__(self, name): - super().__init__(name) - self.logger = logging.getLogger('buildtask') - - -class Assemble(BuildTask): - """ Task that can runs the assembler over the source and enters the - output into an object file """ - def __init__(self, source, target, output_object): - super().__init__('Assemble') - self.source = source - self.output = output_object - self.ostream = BinaryOutputStream(self.output) - self.assembler = Assembler(target) - - def run(self): - self.ostream.selectSection('code') - self.assembler.assemble(self.source, self.ostream) - - -class Compile(BuildTask): - """ Task that compiles C3 source for some target into an object file """ - def __init__(self, sources, includes, target, output_object): - super().__init__('Compile') - self.sources = sources - self.includes = includes - self.target = target - self.output = output_object - - def run(self): - self.logger.debug('Compile started') - diag = DiagnosticsManager() - c3b = Builder(diag, self.target) - cg = CodeGenerator(self.target) - - for ircode in c3b.build(self.sources, self.includes): - if not ircode: - return - - d = {'ircode':ircode} - self.logger.debug('Verifying code {}'.format(ircode), extra=d) - Verifier().verify(ircode) - - # Optimization passes: - CleanPass().run(ircode) - Verifier().verify(ircode) - RemoveAddZero().run(ircode) - Verifier().verify(ircode) - CleanPass().run(ircode) - Verifier().verify(ircode) - - # Code generation: - d = {'ircode':ircode} - self.logger.debug('Starting code generation for {}'.format(ircode), extra=d) - o = BinaryOutputStream(self.output) - cg.generate(ircode, o) - - if not c3b.ok: - diag.printErrors() - raise TaskError('Compile errors') - - -class Link(BuildTask): - """ Link together a collection of object files """ - def __init__(self, objects, layout, output_file): - super().__init__('Link') - self.objects = objects - self.linker = Linker() - self.duration = 0.1337 - self.layout = layout - self.output_file = output_file - - def run(self): - try: - output_obj = self.linker.link(self.objects, self.layout) - except CompilerError as e: - raise TaskError(e.msg) - code = output_obj.get_section('code').data - with open(self.output_file, 'wb') as f: - f.write(code) - - -class ObjCopy(BuildTask): - def __init__(self, objects, output_file): - super().__init__('ObjCopy') -
--- a/python/ppci/c3/__init__.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -""" This is the C3 language front end. """ - -from .parser import Parser -from .lexer import Lexer -from .codegenerator import CodeGenerator -from .visitor import Visitor -from .visitor import AstPrinter -from .builder import Builder
--- a/python/ppci/c3/astnodes.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,376 +0,0 @@ -""" -AST (abstract syntax tree) nodes for the c3 language. -The tree is build by the parser. -Then it is checked -Finally code is generated from it. -""" - -from ppci import SourceLocation - - -class Node: - """ Base class of all nodes in a AST """ - pass - - -# Variables, parameters, local variables, constants and named types: -class Symbol(Node): - """ Symbol is the base class for all named things like variables, - functions, constants and types and modules """ - def __init__(self, name): - self.name = name - self.refs = [] - - def addRef(self, r): - self.refs.append(r) - - @property - def References(self): - return self.refs - - -# Modules -class Package(Symbol): - def __init__(self, name, loc): - super().__init__(name) - self.loc = loc - self.declarations = [] - self.imports = [] - - def add_declaration(self, decl): - self.declarations.append(decl) - if isinstance(decl, Function): - decl.package = self - - def __repr__(self): - return 'MODULE {}'.format(self.name) - - -class Type(Node): - """ Base class of all types """ - pass - - -class NamedType(Type, Symbol): - """ Some types are named, for example a user defined type (typedef) - and built in types. That is why this class derives from both Type - and Symbol. """ - def __init__(self, name): - Symbol.__init__(self, name) - - -class BaseType(NamedType): - """ Built in type """ - def __init__(self, name): - super().__init__(name) - - def __repr__(self): - return '{}'.format(self.name) - - -class FunctionType(Type): - """ Function blueprint, defines argument types and return type """ - def __init__(self, parametertypes, returntype): - self.parametertypes = parametertypes - self.returntype = returntype - - def __repr__(self): - params = ', '.join([str(v) for v in self.parametertypes]) - return '{1} f({0})'.format(params, self.returntype) - - -class PointerType(Type): - """ A type that points to data of some other type """ - def __init__(self, ptype): - assert isinstance(ptype, Type) or isinstance(ptype, Expression) - self.ptype = ptype - - def __repr__(self): - return '({}*)'.format(self.ptype) - - -class StructField: - """ Field of a struct type """ - def __init__(self, name, typ): - assert type(name) is str - self.name = name - self.typ = typ - - -class StructureType(Type): - """ Struct type consisting of several named members """ - def __init__(self, mems): - self.mems = mems - assert all(type(mem) is StructField for mem in mems) - - def hasField(self, name): - for mem in self.mems: - if name == mem.name: - return True - return False - - def fieldType(self, name): - return self.findField(name).typ - - def fieldOffset(self, name): - return self.findField(name).offset - - def findField(self, name): - for mem in self.mems: - if name == mem.name: - return mem - raise KeyError(name) - - def __repr__(self): - return 'STRUCT' - - -class DefinedType(NamedType): - """ A named type indicating another type """ - def __init__(self, name, typ, loc): - assert isinstance(name, str) - super().__init__(name) - self.typ = typ - self.loc = loc - - def __repr__(self): - return 'Named type {0} of type {1}'.format(self.name, self.typ) - - -class Constant(Symbol): - """ Constant definition """ - def __init__(self, name, typ, value): - super().__init__(name) - self.typ = typ - self.value = value - - def __repr__(self): - return 'CONSTANT {0} = {1}'.format(self.name, self.value) - - -class Variable(Symbol): - def __init__(self, name, typ): - super().__init__(name) - self.typ = typ - self.isLocal = False - self.isParameter = False - - def __repr__(self): - return 'Var {} [{}]'.format(self.name, self.typ) - - -class LocalVariable(Variable): - def __init__(self, name, typ): - super().__init__(name, typ) - self.isLocal = True - - -class FormalParameter(Variable): - def __init__(self, name, typ): - super().__init__(name, typ) - self.isParameter = True - - -# Procedure types -class Function(Symbol): - """ Actual implementation of a function """ - def __init__(self, name, loc): - super().__init__(name) - self.loc = loc - self.declarations = [] - - def add_declaration(self, decl): - self.declarations.append(decl) - - def __repr__(self): - return 'Func {}'.format(self.name) - - -# Operations / Expressions: -class Expression(Node): - def __init__(self, loc): - self.loc = loc - - -class Deref(Expression): - def __init__(self, ptr, loc): - super().__init__(loc) - assert isinstance(ptr, Expression) - self.ptr = ptr - - def __repr__(self): - return 'DEREF {}'.format(self.ptr) - - -class TypeCast(Expression): - def __init__(self, to_type, x, loc): - super().__init__(loc) - self.to_type = to_type - self.a = x - - def __repr__(self): - return 'TYPECAST {}'.format(self.to_type) - - -class Member(Expression): - """ Field reference of some object, can also be package selection """ - def __init__(self, base, field, loc): - super().__init__(loc) - assert isinstance(base, Expression) - assert isinstance(field, str) - self.base = base - self.field = field - - def __repr__(self): - return 'MEMBER {}.{}'.format(self.base, self.field) - - -class Unop(Expression): - """ Operation on one operand """ - def __init__(self, op, a, loc): - super().__init__(loc) - assert isinstance(a, Expression) - assert isinstance(op, str) - self.a = a - self.op = op - - def __repr__(self): - return 'UNOP {}'.format(self.op) - - -class Binop(Expression): - """ Expression taking two operands and one operator """ - def __init__(self, a, op, b, loc): - super().__init__(loc) - assert isinstance(a, Expression), type(a) - assert isinstance(b, Expression) - assert isinstance(op, str) - self.a = a - self.b = b - self.op = op # Operation: '+', '-', '*', '/', 'mod' - - def __repr__(self): - return 'BINOP {}'.format(self.op) - - -class Identifier(Expression): - """ Reference to some identifier, can be anything from package, variable - function or type, any named thing! """ - def __init__(self, target, loc): - super().__init__(loc) - self.target = target - - def __repr__(self): - return 'ID {}'.format(self.target) - - -class Literal(Expression): - """ Constant value or string """ - def __init__(self, val, loc): - super().__init__(loc) - self.val = val - - def __repr__(self): - return 'LITERAL {}'.format(self.val) - - -class FunctionCall(Expression): - """ Call to a some function """ - def __init__(self, proc, args, loc): - super().__init__(loc) - self.proc = proc - self.args = args - - def __repr__(self): - return 'CALL {0} '.format(self.proc) - - -# Statements -class Statement(Node): - """ Base class of all statements """ - def __init__(self, loc): - self.loc = loc - - -class Empty(Statement): - """ Empty statement which does nothing! """ - def __init__(self): - super().__init__(None) - - def __repr__(self): - return 'NOP' - - -class Compound(Statement): - """ Statement consisting of a sequence of other statements """ - def __init__(self, statements): - super().__init__(None) - self.statements = statements - for s in self.statements: - assert isinstance(s, Statement) - - def __repr__(self): - return 'COMPOUND STATEMENT' - - -class Return(Statement): - def __init__(self, expr, loc): - super().__init__(loc) - self.expr = expr - - def __repr__(self): - return 'RETURN STATEMENT' - - -class Assignment(Statement): - def __init__(self, lval, rval, loc): - super().__init__(loc) - assert isinstance(lval, Expression) - assert isinstance(rval, Expression) - self.lval = lval - self.rval = rval - - def __repr__(self): - return 'ASSIGNMENT' - - -class ExpressionStatement(Statement): - def __init__(self, ex, loc): - super().__init__(loc) - self.ex = ex - - def __repr__(self): - return 'Epression' - - -class If(Statement): - def __init__(self, condition, truestatement, falsestatement, loc): - super().__init__(loc) - self.condition = condition - self.truestatement = truestatement - self.falsestatement = falsestatement - - def __repr__(self): - return 'IF-statement' - - -class While(Statement): - def __init__(self, condition, statement, loc): - super().__init__(loc) - self.condition = condition - self.statement = statement - - def __repr__(self): - return 'WHILE-statement' - - -class For(Statement): - def __init__(self, init, condition, final, statement, loc): - super().__init__(loc) - self.init = init - self.condition = condition - self.final = final - self.statement = statement - - def __repr__(self): - return 'FOR-statement'
--- a/python/ppci/c3/builder.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,133 +0,0 @@ -import logging -from .lexer import Lexer -from .parser import Parser -from .codegenerator import CodeGenerator -from .scope import createTopScope, Scope -from .visitor import AstPrinter, Visitor -from .astnodes import Package, Function, Identifier, Symbol - - -class C3Pass: - def __init__(self, diag): - self.diag = diag - self.logger = logging.getLogger('c3') - self.visitor = Visitor() - - def error(self, msg, loc=None): - self.pkg.ok = False - self.diag.error(msg, loc) - - def visit(self, pkg, pre, post): - self.visitor.visit(pkg, pre, post) - - -class ScopeFiller(C3Pass): - scoped_types = [Package, Function] - - def __init__(self, diag, topScope, packages): - super().__init__(diag) - self.topScope = topScope - self.packages = packages - - """ Scope is attached to the correct modules. """ - def addScope(self, pkg): - self.logger.debug('Adding scoping to package {}'.format(pkg.name)) - self.pkg = pkg - # Prepare top level scope and set scope to all objects: - self.scopeStack = [self.topScope] - modScope = Scope(self.CurrentScope) - self.scopeStack.append(modScope) - self.visit(pkg, self.enterScope, self.quitScope) - assert len(self.scopeStack) == 2 - - self.logger.debug('Resolving imports for package {}'.format(pkg.name)) - # Handle imports: - for i in pkg.imports: - if i not in self.packages: - self.error('Cannot import {}'.format(i)) - continue - pkg.scope.addSymbol(self.packages[i]) - - @property - def CurrentScope(self): - return self.scopeStack[-1] - - def addSymbol(self, sym): - if self.CurrentScope.hasSymbol(sym.name): - self.error('Redefinition of {0}'.format(sym.name), sym.loc) - else: - self.CurrentScope.addSymbol(sym) - - def enterScope(self, sym): - # Attach scope to references: - if type(sym) is Identifier: - sym.scope = self.CurrentScope - - # Add symbols to current scope: - if isinstance(sym, Symbol): - self.addSymbol(sym) - sym.scope = self.CurrentScope - - # Create subscope for items creating a scope: - if type(sym) in self.scoped_types: - newScope = Scope(self.CurrentScope) - self.scopeStack.append(newScope) - sym.innerScope = self.CurrentScope - - def quitScope(self, sym): - # Pop out of scope: - if type(sym) in self.scoped_types: - self.scopeStack.pop(-1) - - -class Builder: - """ - Generates IR-code from c3 source. - Reports errors to the diagnostics system. - """ - def __init__(self, diag, target): - self.logger = logging.getLogger('c3') - self.diag = diag - self.lexer = Lexer(diag) - self.parser = Parser(diag) - self.cg = CodeGenerator(diag) - self.topScope = createTopScope(target) # Scope with built in types - - def build(self, srcs, imps=[]): - """ Create IR-code from sources """ - self.logger.debug('Building {} source files'.format(len(srcs))) - iter(srcs) # Check if srcs are iterable - iter(imps) - self.ok = True - self.pkgs = {} - - # Parsing stage (phase 1) - def doParse(src): - tokens = self.lexer.lex(src) - return self.parser.parseSource(tokens) - s_pkgs = set(map(doParse, srcs)) - i_pkgs = set(map(doParse, imps)) - all_pkgs = s_pkgs | i_pkgs - if not all(all_pkgs): - self.ok = False - return - - # Fix scopes and package refs (phase 1.5) - packages = {pkg.name: pkg for pkg in all_pkgs} - self.pkgs = packages - - scopeFiller = ScopeFiller(self.diag, self.topScope, packages) - # Fix scopes: - for pkg in all_pkgs: - scopeFiller.addScope(pkg) - if not all(pkg.ok for pkg in all_pkgs): - self.ok = False - return - - # Generate intermediate code (phase 2) - # Only return ircode when everything is OK - for pkg in all_pkgs & s_pkgs: - yield self.cg.gencode(pkg) - if not all(pkg.ok for pkg in all_pkgs): - self.ok = False - return
--- a/python/ppci/c3/codegenerator.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,400 +0,0 @@ -import logging -from .. import ir -from .. import irutils -from . import astnodes as ast - - -class SemanticError(Exception): - """ Error thrown when a semantic issue is observed """ - def __init__(self, msg, loc): - super().__init__() - self.msg = msg - self.loc = loc - - -class CodeGenerator(irutils.Builder): - """ - Generates intermediate (IR) code from a package. The entry function is - 'genModule'. The main task of this part is to rewrite complex control - structures, such as while and for loops into simple conditional - jump statements. Also complex conditional statements are simplified. - Such as 'and' and 'or' statements are rewritten in conditional jumps. - And structured datatypes are rewritten. - - Type checking is done in one run with code generation. - """ - def __init__(self, diag): - self.logger = logging.getLogger('c3cgen') - self.diag = diag - - def gencode(self, pkg): - """ Generate code for a single module """ - self.prepare() - assert type(pkg) is ast.Package - self.pkg = pkg - self.intType = pkg.scope['int'] - self.boolType = pkg.scope['bool'] - self.logger.debug('Generating ir-code for {}'.format(pkg.name), extra={'c3_ast':pkg}) - self.varMap = {} # Maps variables to storage locations. - self.funcMap = {} - self.m = ir.Module(pkg.name) - try: - for s in pkg.innerScope.Functions: - f = self.newFunction(s.name) - self.funcMap[s] = f - for v in pkg.innerScope.Variables: - self.varMap[v] = self.newTemp() - for s in pkg.innerScope.Functions: - self.gen_function(s) - except SemanticError as e: - self.error(e.msg, e.loc) - if self.pkg.ok: - return self.m - - def error(self, msg, loc=None): - self.pkg.ok = False - self.diag.error(msg, loc) - - def gen_function(self, fn): - # TODO: handle arguments - f = self.funcMap[fn] - f.return_value = self.newTemp() - self.setFunction(f) - l2 = self.newBlock() - self.emit(ir.Jump(l2)) - self.setBlock(l2) - # generate room for locals: - - for sym in fn.innerScope: - self.the_type(sym.typ) - if sym.isParameter: - p = ir.Parameter(sym.name) - variable = ir.LocalVariable(sym.name + '_copy') - f.addParameter(p) - f.addLocal(variable) - # Move parameter into local copy: - self.emit(ir.Move(ir.Mem(variable), p)) - elif sym.isLocal: - variable = ir.LocalVariable(sym.name) - f.addLocal(variable) - elif isinstance(sym, ast.Variable): - variable = ir.LocalVariable(sym.name) - f.addLocal(variable) - else: - raise NotImplementedError('{}'.format(sym)) - self.varMap[sym] = variable - - self.genCode(fn.body) - self.emit(ir.Move(f.return_value, ir.Const(0))) - self.emit(ir.Jump(f.epiloog)) - self.setFunction(None) - - def genCode(self, code): - """ Wrapper around gen_stmt to catch errors """ - try: - self.gen_stmt(code) - except SemanticError as e: - self.error(e.msg, e.loc) - - def gen_stmt(self, code): - """ Generate code for a statement """ - assert isinstance(code, ast.Statement) - self.setLoc(code.loc) - if type(code) is ast.Compound: - for s in code.statements: - self.genCode(s) - elif type(code) is ast.Empty: - pass - elif type(code) is ast.Assignment: - lval = self.genExprCode(code.lval) - rval = self.genExprCode(code.rval) - if not self.equalTypes(code.lval.typ, code.rval.typ): - msg = 'Cannot assign {} to {}'.format(code.rval.typ, code.lval.typ) - raise SemanticError(msg, code.loc) - if not code.lval.lvalue: - raise SemanticError('No valid lvalue {}'.format(code.lval), code.lval.loc) - self.emit(ir.Move(lval, rval)) - elif type(code) is ast.ExpressionStatement: - self.emit(ir.Exp(self.genExprCode(code.ex))) - elif type(code) is ast.If: - bbtrue = self.newBlock() - bbfalse = self.newBlock() - te = self.newBlock() - self.gen_cond_code(code.condition, bbtrue, bbfalse) - self.setBlock(bbtrue) - self.genCode(code.truestatement) - self.emit(ir.Jump(te)) - self.setBlock(bbfalse) - self.genCode(code.falsestatement) - self.emit(ir.Jump(te)) - self.setBlock(te) - elif type(code) is ast.Return: - re = self.genExprCode(code.expr) - self.emit(ir.Move(self.fn.return_value, re)) - self.emit(ir.Jump(self.fn.epiloog)) - b = self.newBlock() - self.setBlock(b) - elif type(code) is ast.While: - bbdo = self.newBlock() - bbtest = self.newBlock() - te = self.newBlock() - self.emit(ir.Jump(bbtest)) - self.setBlock(bbtest) - self.gen_cond_code(code.condition, bbdo, te) - self.setBlock(bbdo) - self.genCode(code.statement) - self.emit(ir.Jump(bbtest)) - self.setBlock(te) - elif type(code) is ast.For: - bbdo = self.newBlock() - bbtest = self.newBlock() - te = self.newBlock() - self.genCode(code.init) - self.emit(ir.Jump(bbtest)) - self.setBlock(bbtest) - self.gen_cond_code(code.condition, bbdo, te) - self.setBlock(bbdo) - self.genCode(code.statement) - self.emit(ir.Jump(bbtest)) - self.setBlock(te) - else: - raise NotImplementedError('Unknown stmt {}'.format(code)) - - def gen_cond_code(self, expr, bbtrue, bbfalse): - """ Generate conditional logic. - Implement sequential logical operators. """ - if type(expr) is ast.Binop: - if expr.op == 'or': - l2 = self.newBlock() - self.gen_cond_code(expr.a, bbtrue, l2) - if not self.equalTypes(expr.a.typ, self.boolType): - raise SemanticError('Must be boolean', expr.a.loc) - self.setBlock(l2) - self.gen_cond_code(expr.b, bbtrue, bbfalse) - if not self.equalTypes(expr.b.typ, self.boolType): - raise SemanticError('Must be boolean', expr.b.loc) - elif expr.op == 'and': - l2 = self.newBlock() - self.gen_cond_code(expr.a, l2, bbfalse) - if not self.equalTypes(expr.a.typ, self.boolType): - self.error('Must be boolean', expr.a.loc) - self.setBlock(l2) - self.gen_cond_code(expr.b, bbtrue, bbfalse) - if not self.equalTypes(expr.b.typ, self.boolType): - raise SemanticError('Must be boolean', expr.b.loc) - elif expr.op in ['==', '>', '<', '!=', '<=', '>=']: - ta = self.genExprCode(expr.a) - tb = self.genExprCode(expr.b) - if not self.equalTypes(expr.a.typ, expr.b.typ): - raise SemanticError('Types unequal {} != {}' - .format(expr.a.typ, expr.b.typ), expr.loc) - self.emit(ir.CJump(ta, expr.op, tb, bbtrue, bbfalse)) - else: - raise SemanticError('non-bool: {}'.format(expr.op), expr.loc) - expr.typ = self.boolType - elif type(expr) is ast.Literal: - self.genExprCode(expr) - if expr.val: - self.emit(ir.Jump(bbtrue)) - else: - self.emit(ir.Jump(bbfalse)) - else: - raise NotImplementedError('Unknown cond {}'.format(expr)) - if not self.equalTypes(expr.typ, self.boolType): - self.error('Condition must be boolean', expr.loc) - - def genExprCode(self, expr): - """ Generate code for an expression. Return the generated ir-value """ - assert isinstance(expr, ast.Expression) - if type(expr) is ast.Binop: - expr.lvalue = False - if expr.op in ['+', '-', '*', '/', '<<', '>>', '|', '&']: - ra = self.genExprCode(expr.a) - rb = self.genExprCode(expr.b) - if self.equalTypes(expr.a.typ, self.intType) and \ - self.equalTypes(expr.b.typ, self.intType): - expr.typ = expr.a.typ - else: - raise SemanticError('Can only add integers', expr.loc) - else: - raise NotImplementedError("Cannot use equality as expressions") - return ir.Binop(ra, expr.op, rb) - elif type(expr) is ast.Unop: - if expr.op == '&': - ra = self.genExprCode(expr.a) - expr.typ = ast.PointerType(expr.a.typ) - if not expr.a.lvalue: - raise SemanticError('No valid lvalue', expr.a.loc) - expr.lvalue = False - assert type(ra) is ir.Mem - return ra.e - else: - raise NotImplementedError('Unknown unop {0}'.format(expr.op)) - elif type(expr) is ast.Identifier: - # Generate code for this identifier. - tg = self.resolveSymbol(expr) - expr.kind = type(tg) - expr.typ = tg.typ - # This returns the dereferenced variable. - if isinstance(tg, ast.Variable): - expr.lvalue = True - return ir.Mem(self.varMap[tg]) - elif isinstance(tg, ast.Constant): - c_val = self.genExprCode(tg.value) - return self.evalConst(c_val) - else: - raise NotImplementedError(str(tg)) - elif type(expr) is ast.Deref: - # dereference pointer type: - addr = self.genExprCode(expr.ptr) - ptr_typ = self.the_type(expr.ptr.typ) - expr.lvalue = True - if type(ptr_typ) is ast.PointerType: - expr.typ = ptr_typ.ptype - return ir.Mem(addr) - else: - raise SemanticError('Cannot deref non-pointer', expr.loc) - elif type(expr) is ast.Member: - base = self.genExprCode(expr.base) - expr.lvalue = expr.base.lvalue - basetype = self.the_type(expr.base.typ) - if type(basetype) is ast.StructureType: - if basetype.hasField(expr.field): - expr.typ = basetype.fieldType(expr.field) - else: - raise SemanticError('{} does not contain field {}' - .format(basetype, expr.field), expr.loc) - else: - raise SemanticError('Cannot select {} of non-structure type {}' - .format(expr.field, basetype), expr.loc) - - assert type(base) is ir.Mem, type(base) - bt = self.the_type(expr.base.typ) - offset = ir.Const(bt.fieldOffset(expr.field)) - return ir.Mem(ir.Add(base.e, offset)) - elif type(expr) is ast.Literal: - expr.lvalue = False - typemap = {int: 'int', float: 'double', bool: 'bool', str:'string'} - if type(expr.val) in typemap: - expr.typ = self.pkg.scope[typemap[type(expr.val)]] - else: - raise SemanticError('Unknown literal type {}'.format(expr.val), expr.loc) - return ir.Const(expr.val) - elif type(expr) is ast.TypeCast: - return self.gen_type_cast(expr) - elif type(expr) is ast.FunctionCall: - return self.gen_function_call(expr) - else: - raise NotImplementedError('Unknown expr {}'.format(expr)) - - def gen_type_cast(self, expr): - """ Generate code for type casting """ - ar = self.genExprCode(expr.a) - from_type = self.the_type(expr.a.typ) - to_type = self.the_type(expr.to_type) - if isinstance(from_type, ast.PointerType) and isinstance(to_type, ast.PointerType): - expr.typ = expr.to_type - return ar - elif type(from_type) is ast.BaseType and from_type.name == 'int' and \ - isinstance(to_type, ast.PointerType): - expr.typ = expr.to_type - return ar - else: - raise SemanticError('Cannot cast {} to {}' - .format(from_type, to_type), expr.loc) - - def gen_function_call(self, expr): - """ Generate code for a function call """ - # Evaluate the arguments: - args = [self.genExprCode(e) for e in expr.args] - # Check arguments: - tg = self.resolveSymbol(expr.proc) - if type(tg) is not ast.Function: - raise SemanticError('cannot call {}'.format(tg)) - ftyp = tg.typ - fname = tg.package.name + '_' + tg.name - ptypes = ftyp.parametertypes - if len(expr.args) != len(ptypes): - raise SemanticError('{} requires {} arguments, {} given' - .format(fname, len(ptypes), len(expr.args)), expr.loc) - for arg, at in zip(expr.args, ptypes): - if not self.equalTypes(arg.typ, at): - raise SemanticError('Got {}, expected {}' - .format(arg.typ, at), arg.loc) - # determine return type: - expr.typ = ftyp.returntype - return ir.Call(fname, args) - - def evalConst(self, c): - if isinstance(c, ir.Const): - return c - else: - raise SemanticError('Cannot evaluate constant {}'.format(c)) - - def resolveSymbol(self, sym): - if type(sym) is ast.Member: - base = self.resolveSymbol(sym.base) - if type(base) is not ast.Package: - raise SemanticError('Base is not a package', sym.loc) - scope = base.innerScope - name = sym.field - elif type(sym) is ast.Identifier: - scope = sym.scope - name = sym.target - else: - raise NotImplementedError(str(sym)) - if name in scope: - s = scope[name] - else: - raise SemanticError('{} undefined'.format(name), sym.loc) - assert isinstance(s, ast.Symbol) - return s - - def size_of(self, t): - """ Determine the byte size of a type """ - t = self.the_type(t) - if type(t) is ast.BaseType: - return t.bytesize - elif type(t) is ast.StructureType: - return sum(self.size_of(mem.typ) for mem in t.mems) - else: - raise NotImplementedError(str(t)) - - def the_type(self, t): - """ Recurse until a 'real' type is found """ - if type(t) is ast.DefinedType: - t = self.the_type(t.typ) - elif type(t) in [ast.Identifier, ast.Member]: - t = self.the_type(self.resolveSymbol(t)) - elif type(t) is ast.StructureType: - # Setup offsets of fields. Is this the right place?: - offset = 0 - for mem in t.mems: - mem.offset = offset - offset = offset + self.size_of(mem.typ) - elif isinstance(t, ast.Type): - pass - else: - raise NotImplementedError(str(t)) - assert isinstance(t, ast.Type) - return t - - def equalTypes(self, a, b): - """ Compare types a and b for structural equavalence. """ - # Recurse into named types: - a = self.the_type(a) - b = self.the_type(b) - - if type(a) is type(b): - if type(a) is ast.BaseType: - return a.name == b.name - elif type(a) is ast.PointerType: - return self.equalTypes(a.ptype, b.ptype) - elif type(a) is ast.StructureType: - if len(a.mems) != len(b.mems): - return False - return all(self.equalTypes(am.typ, bm.typ) for am, bm in - zip(a.mems, b.mems)) - else: - raise NotImplementedError('{} not implemented'.format(type(a))) - return False
--- a/python/ppci/c3/lexer.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ -import re -from ppci import CompilerError, SourceLocation, Token -import baselex - -""" - Lexical analyzer part. Splits the input character stream into tokens. -""" - -keywords = ['and', 'or', 'not', 'true', 'false', - 'else', 'if', 'while', 'for', 'return', - 'function', 'var', 'type', 'const', - 'struct', 'cast', - 'import', 'module'] - - -class Lexer: - """ Generates a sequence of token from an input stream """ - def __init__(self, diag): - self.diag = diag - - def lex(self, source): - return self.tokenize(source) - - def tokenize(self, input_file): - """ - Tokenizer, generates an iterator that - returns tokens! - - Input is a file like object. - - This GREAT example was taken from python re doc page! - """ - filename = input_file.name if hasattr(input_file, 'name') else '' - s = input_file.read() - input_file.close() - self.diag.addSource(filename, s) - tok_spec = [ - ('REAL', r'\d+\.\d+'), - ('HEXNUMBER', r'0x[\da-fA-F]+'), - ('NUMBER', r'\d+'), - ('ID', r'[A-Za-z][A-Za-z\d_]*'), - ('NEWLINE', r'\n'), - ('SKIP', r'[ \t]'), - ('COMMENTS', r'//.*'), - ('LONGCOMMENTBEGIN', r'\/\*'), - ('LONGCOMMENTEND', r'\*\/'), - ('LEESTEKEN', r'==|->|<<|>>|!=|\+\+|[\.,=:;\-+*\[\]/\(\)]|>=|<=|<>|>|<|{|}|&|\^|\|'), - ('STRING', r"'.*?'") - ] - tok_re = '|'.join('(?P<%s>%s)' % pair for pair in tok_spec) - gettok = re.compile(tok_re).match - line = 1 - pos = line_start = 0 - mo = gettok(s) - incomment = False - while mo is not None: - typ = mo.lastgroup - val = mo.group(typ) - if typ == 'NEWLINE': - line_start = pos - line += 1 - elif typ == 'COMMENTS': - pass - elif typ == 'LONGCOMMENTBEGIN': - incomment = True - elif typ == 'LONGCOMMENTEND': - incomment = False - elif typ == 'SKIP': - pass - elif incomment: - pass # Wait until we are not in a comment section - else: - if typ == 'ID': - if val in keywords: - typ = val - elif typ == 'LEESTEKEN': - typ = val - elif typ == 'NUMBER': - val = int(val) - elif typ == 'HEXNUMBER': - val = int(val[2:], 16) - typ = 'NUMBER' - elif typ == 'REAL': - val = float(val) - elif typ == 'STRING': - val = val[1:-1] - loc = SourceLocation(filename, line, mo.start() - line_start, - mo.end() - mo.start()) - yield Token(typ, val, loc) - pos = mo.end() - mo = gettok(s, pos) - if pos != len(s): - col = pos - line_start - loc = SourceLocation(filename, line, col, 1) - raise CompilerError('Unexpected: "{0}"'.format(s[pos]), loc) - loc = SourceLocation(filename, line, 0, 0) - yield Token('END', '', loc)
--- a/python/ppci/c3/parser.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,412 +0,0 @@ -import logging -from ppci import CompilerError -from .astnodes import Member, Literal, TypeCast, Unop, Binop -from .astnodes import Assignment, ExpressionStatement, Compound -from .astnodes import Return, While, If, Empty, For -from .astnodes import FunctionType, Function, FormalParameter -from .astnodes import StructureType, DefinedType, PointerType -from .astnodes import Constant, Variable -from .astnodes import StructField, Deref -from .astnodes import Package -from .astnodes import Identifier -from .astnodes import FunctionCall - - -class Parser: - """ Parses sourcecode into an abstract syntax tree (AST) """ - def __init__(self, diag): - self.logger = logging.getLogger('c3') - self.diag = diag - - def parseSource(self, tokens): - self.logger.debug('Parsing source') - self.tokens = tokens - self.token = self.tokens.__next__() - try: - self.parsePackage() - self.mod.ok = True # Valid until proven wrong :) - return self.mod - except CompilerError as e: - self.diag.addDiag(e) - - def Error(self, msg): - raise CompilerError(msg, self.token.loc) - - # Lexer helpers: - def Consume(self, typ): - if self.Peak == typ: - return self.NextToken() - else: - self.Error('Excected: "{0}", got "{1}"'.format(typ, self.Peak)) - - @property - def Peak(self): - return self.token.typ - - @property - def CurLoc(self): - return self.token.loc - - def hasConsumed(self, typ): - if self.Peak == typ: - self.Consume(typ) - return True - return False - - def NextToken(self): - t = self.token - if t.typ != 'END': - self.token = self.tokens.__next__() - return t - - def addDeclaration(self, decl): - self.currentPart.add_declaration(decl) - - def parseImport(self): - self.Consume('import') - name = self.Consume('ID').val - self.mod.imports.append(name) - self.Consume(';') - - def parsePackage(self): - self.Consume('module') - name = self.Consume('ID') - self.Consume(';') - self.mod = Package(name.val, name.loc) - self.currentPart = self.mod - while self.Peak != 'END': - self.parseTopLevel() - self.Consume('END') - - def parseTopLevel(self): - if self.Peak == 'function': - self.parseFunctionDef() - elif self.Peak == 'var': - self.parseVarDef() - # TODO handle variable initialization - elif self.Peak == 'const': - self.parseConstDef() - elif self.Peak == 'type': - self.parseTypeDef() - elif self.Peak == 'import': - self.parseImport() - else: - self.Error('Expected function, var, const or type') - - def parseDesignator(self): - """ A designator designates an object with a name. """ - name = self.Consume('ID') - return Identifier(name.val, name.loc) - - def parseIdSequence(self): - ids = [self.Consume('ID')] - while self.hasConsumed(','): - ids.append(self.Consume('ID')) - return ids - - # Type system - def parseTypeSpec(self): - # For now, do simple type spec, just parse an ID: - if self.Peak == 'struct': - self.Consume('struct') - self.Consume('{') - mems = [] - while self.Peak != '}': - mem_t = self.parseTypeSpec() - for i in self.parseIdSequence(): - mems.append(StructField(i.val, mem_t)) - self.Consume(';') - self.Consume('}') - theT = StructureType(mems) - elif self.Peak == 'enum': - # TODO) - raise NotImplementedError() - else: - theT = self.PostFixExpression() - # Check for pointer suffix: - while self.hasConsumed('*'): - theT = PointerType(theT) - return theT - - def parseTypeDef(self): - self.Consume('type') - newtype = self.parseTypeSpec() - typename = self.Consume('ID') - self.Consume(';') - df = DefinedType(typename.val, newtype, typename.loc) - self.addDeclaration(df) - - # Variable declarations: - def parseVarDef(self): - self.Consume('var') - t = self.parseTypeSpec() - for name in self.parseIdSequence(): - v = Variable(name.val, t) - v.loc = name.loc - self.addDeclaration(v) - self.Consume(';') - return Empty() - - def parseConstDef(self): - self.Consume('const') - t = self.parseTypeSpec() - while True: - name = self.Consume('ID') - self.Consume('=') - val = self.Expression() - c = Constant(name.val, t, val) - self.addDeclaration(c) - c.loc = name.loc - if not self.hasConsumed(','): - break - self.Consume(';') - - def parseFunctionDef(self): - loc = self.Consume('function').loc - returntype = self.parseTypeSpec() - fname = self.Consume('ID').val - f = Function(fname, loc) - self.addDeclaration(f) - savePart = self.currentPart - self.currentPart = f - self.Consume('(') - parameters = [] - if not self.hasConsumed(')'): - while True: - typ = self.parseTypeSpec() - name = self.Consume('ID') - param = FormalParameter(name.val, typ) - param.loc = name.loc - self.addDeclaration(param) - parameters.append(param) - if not self.hasConsumed(','): - break - self.Consume(')') - paramtypes = [p.typ for p in parameters] - f.typ = FunctionType(paramtypes, returntype) - f.body = self.parseCompound() - self.currentPart = savePart - - def parseIf(self): - loc = self.Consume('if').loc - self.Consume('(') - condition = self.Expression() - self.Consume(')') - yes = self.Statement() - no = self.Statement() if self.hasConsumed('else') else Empty() - return If(condition, yes, no, loc) - - def parseWhile(self): - loc = self.Consume('while').loc - self.Consume('(') - condition = self.Expression() - self.Consume(')') - statements = self.Statement() - return While(condition, statements, loc) - - def parseFor(self): - loc = self.Consume('for').loc - self.Consume('(') - init = self.Statement() - self.Consume(';') - condition = self.Expression() - self.Consume(';') - final = self.Statement() - self.Consume(')') - statements = self.Statement() - return For(init, condition, final, statements, loc) - - def parseReturn(self): - loc = self.Consume('return').loc - if self.Peak == ';': - expr = Literal(0, loc) - else: - expr = self.Expression() - self.Consume(';') - return Return(expr, loc) - - def parseCompound(self): - self.Consume('{') - statements = [] - while not self.hasConsumed('}'): - statements.append(self.Statement()) - return Compound(statements) - - def Statement(self): - # Determine statement type based on the pending token: - if self.Peak == 'if': - return self.parseIf() - elif self.Peak == 'while': - return self.parseWhile() - elif self.Peak == 'for': - return self.parseFor() - elif self.Peak == '{': - return self.parseCompound() - elif self.hasConsumed(';'): - return Empty() - elif self.Peak == 'var': - return self.parseVarDef() - elif self.Peak == 'return': - return self.parseReturn() - else: - x = self.UnaryExpression() - if self.Peak == '=': - # We enter assignment mode here. - loc = self.Consume('=').loc - rhs = self.Expression() - return Assignment(x, rhs, loc) - else: - return ExpressionStatement(x, x.loc) - - # Expression section: - # We not implement these C constructs: - # a(2), f = 2 - # and this: - # a = 2 < x : 4 ? 1; - - def Expression(self): - exp = self.LogicalAndExpression() - while self.Peak == 'or': - loc = self.Consume('or').loc - e2 = self.LogicalAndExpression() - exp = Binop(exp, 'or', e2, loc) - return exp - - def LogicalAndExpression(self): - o = self.EqualityExpression() - while self.Peak == 'and': - loc = self.Consume('and').loc - o2 = self.EqualityExpression() - o = Binop(o, 'and', o2, loc) - return o - - def EqualityExpression(self): - ee = self.SimpleExpression() - while self.Peak in ['<', '==', '>', '>=', '<=', '!=']: - op = self.Consume(self.Peak) - ee2 = self.SimpleExpression() - ee = Binop(ee, op.typ, ee2, op.loc) - return ee - - def SimpleExpression(self): - """ Shift operations before + and - ? """ - e = self.AddExpression() - while self.Peak in ['>>', '<<']: - op = self.Consume(self.Peak) - e2 = self.AddExpression() - e = Binop(e, op.typ, e2, op.loc) - return e - - def AddExpression(self): - e = self.Term() - while self.Peak in ['+', '-']: - op = self.Consume(self.Peak) - e2 = self.Term() - e = Binop(e, op.typ, e2, op.loc) - return e - - def Term(self): - t = self.BitwiseOr() - while self.Peak in ['*', '/']: - op = self.Consume(self.Peak) - t2 = self.BitwiseOr() - t = Binop(t, op.typ, t2, op.loc) - return t - - def BitwiseOr(self): - a = self.BitwiseAnd() - while self.Peak == '|': - op = self.Consume(self.Peak) - b = self.BitwiseAnd() - a = Binop(a, op.typ, b, op.loc) - return a - - def BitwiseAnd(self): - a = self.CastExpression() - while self.Peak == '&': - op = self.Consume(self.Peak) - b = self.CastExpression() - a = Binop(a, op.typ, b, op.loc) - return a - - # Domain of unary expressions: - - def CastExpression(self): - """ - the C-style type cast conflicts with '(' expr ')' - so introduce extra keyword 'cast' - """ - if self.Peak == 'cast': - loc = self.Consume('cast').loc - self.Consume('<') - t = self.parseTypeSpec() - self.Consume('>') - self.Consume('(') - ce = self.Expression() - self.Consume(')') - return TypeCast(t, ce, loc) - else: - return self.UnaryExpression() - - def UnaryExpression(self): - if self.Peak in ['&', '*']: - op = self.Consume(self.Peak) - ce = self.CastExpression() - if op.val == '*': - return Deref(ce, op.loc) - else: - return Unop(op.typ, ce, op.loc) - else: - return self.PostFixExpression() - - def PostFixExpression(self): - pfe = self.PrimaryExpression() - while self.Peak in ['[', '.', '->', '(', '++']: - if self.hasConsumed('['): - raise NotImplementedError('Array not yet implemented') - elif self.hasConsumed('->'): - field = self.Consume('ID') - pfe = Deref(pfe, pfe.loc) - pfe = Member(pfe, field.val, field.loc) - elif self.hasConsumed('.'): - field = self.Consume('ID') - pfe = Member(pfe, field.val, field.loc) - elif self.Peak == '++': - loc = self.Consume('++').loc - pfe = Unop('++', pfe, loc) - elif self.hasConsumed('('): - # Function call - args = [] - if not self.hasConsumed(')'): - args.append(self.Expression()) - while self.hasConsumed(','): - args.append(self.Expression()) - self.Consume(')') - pfe = FunctionCall(pfe, args, pfe.loc) - else: - raise Exception() - return pfe - - def PrimaryExpression(self): - if self.hasConsumed('('): - e = self.Expression() - self.Consume(')') - return e - elif self.Peak == 'NUMBER': - val = self.Consume('NUMBER') - return Literal(val.val, val.loc) - elif self.Peak == 'REAL': - val = self.Consume('REAL') - return Literal(val.val, val.loc) - elif self.Peak == 'true': - val = self.Consume('true') - return Literal(True, val.loc) - elif self.Peak == 'false': - val = self.Consume('false') - return Literal(False, val.loc) - elif self.Peak == 'STRING': - val = self.Consume('STRING') - return Literal(val.val, val.loc) - elif self.Peak == 'ID': - return self.parseDesignator() - self.Error('Expected NUM, ID or (expr), got {0}'.format(self.Peak))
--- a/python/ppci/c3/scope.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -from .astnodes import Constant, Variable, Function, BaseType, Symbol - - -class Scope: - """ A scope contains all symbols in a scope. It also has a parent scope, - when looking for a symbol, also the parent scopes are checked. """ - def __init__(self, parent=None): - self.symbols = {} - self.parent = parent - - def __iter__(self): - # Iterate in a deterministic manner: - return iter(self.Constants + self.Variables + self.Functions) - - @property - def Syms(self): - syms = self.symbols.values() - return sorted(syms, key=lambda v: v.name) - - @property - def Constants(self): - return [s for s in self.Syms if type(s) is Constant] - - @property - def Variables(self): - return [s for s in self.Syms if isinstance(s, Variable)] - - @property - def Functions(self): - return [s for s in self.Syms if type(s) is Function] - - def getSymbol(self, name): - if name in self.symbols: - return self.symbols[name] - # Look for symbol: - elif self.parent: - return self.parent.getSymbol(name) - else: - raise KeyError(name) - - def __getitem__(self, key): - return self.getSymbol(key) - - def hasSymbol(self, name): - if name in self.symbols: - return True - elif self.parent: - return self.parent.hasSymbol(name) - else: - return False - - def __contains__(self, name): - return self.hasSymbol(name) - - def addSymbol(self, sym): - assert sym.name not in self.symbols - assert isinstance(sym, Symbol) - self.symbols[sym.name] = sym - - def __repr__(self): - return 'Scope with {} symbols'.format(len(self.symbols)) - - -def createTopScope(target): - scope = Scope() - for tn in ['u64', 'u32', 'u16', 'u8']: - scope.addSymbol(BaseType(tn)) - # buildin types: - intType = BaseType('int') - intType.bytesize = target.byte_sizes['int'] - scope.addSymbol(intType) - scope.addSymbol(BaseType('double')) - scope.addSymbol(BaseType('void')) - scope.addSymbol(BaseType('bool')) - scope.addSymbol(BaseType('string')) - scope.addSymbol(BaseType('byte')) - return scope
--- a/python/ppci/c3/visitor.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -from .astnodes import * - - -class Visitor: - """ - Visitor that can visit all nodes in the AST - and run pre and post functions. - """ - def visit(self, node, f_pre=None, f_post=None): - self.f_pre = f_pre - self.f_post = f_post - self.do(node) - - def do(self, node): - # Run pre function: - if self.f_pre: - self.f_pre(node) - - # Descent into subnodes: - if type(node) is Package: - for decl in node.declarations: - self.do(decl) - elif type(node) is Function: - for s in node.declarations: - self.do(s) - self.do(node.typ) - self.do(node.body) - elif type(node) is Compound: - for s in node.statements: - self.do(s) - elif type(node) is If: - self.do(node.condition) - self.do(node.truestatement) - self.do(node.falsestatement) - elif type(node) is While: - self.do(node.condition) - self.do(node.statement) - elif type(node) is For: - self.do(node.init) - self.do(node.condition) - self.do(node.final) - self.do(node.statement) - elif type(node) is Assignment: - self.do(node.lval) - self.do(node.rval) - elif type(node) is FunctionCall: - for arg in node.args: - self.do(arg) - self.do(node.proc) - elif type(node) is Return: - self.do(node.expr) - elif type(node) is Binop: - self.do(node.a) - self.do(node.b) - elif type(node) is Unop: - self.do(node.a) - elif type(node) is ExpressionStatement: - self.do(node.ex) - elif type(node) is TypeCast: - self.do(node.a) - self.do(node.to_type) - elif type(node) is Member: - self.do(node.base) - elif type(node) is Deref: - self.do(node.ptr) - elif type(node) is Constant: - self.do(node.typ) - self.do(node.value) - elif type(node) is DefinedType: - self.do(node.typ) - elif isinstance(node, Variable): - self.do(node.typ) - elif type(node) is PointerType: - self.do(node.ptype) - elif type(node) is StructureType: - for m in node.mems: - self.do(m.typ) - elif type(node) is FunctionType: - for pt in node.parametertypes: - self.do(pt) - self.do(node.returntype) - elif type(node) in [Identifier, Literal, Empty]: - # Those nodes do not have child nodes. - pass - else: - raise Exception('Could not visit "{0}"'.format(node)) - - # run post function - if self.f_post: - self.f_post(node) - - -class AstPrinter: - """ Prints an AST as text """ - def printAst(self, pkg, f): - self.indent = 2 - self.f = f - visitor = Visitor() - visitor.visit(pkg, self.print1, self.print2) - - def print1(self, node): - print(' ' * self.indent + str(node), file=self.f) - self.indent += 2 - - def print2(self, node): - self.indent -= 2
--- a/python/ppci/codegen/__init__.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -from .codegen import CodeGenerator
--- a/python/ppci/codegen/canon.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,130 +0,0 @@ -from .. import ir -from .. import irutils -from itertools import chain - -def make(function, frame): - """ - Create canonicalized version of the IR-code. This means: - - Calls out of expressions. - - Other things? - """ - # Change the tree. This modifies the IR-tree! - # Move all parameters into registers - parmoves = [] - for p in function.arguments: - pt = newTemp() - frame.parMap[p] = pt - parmoves.append(ir.Move(pt, frame.argLoc(p.num))) - function.entry.instructions = parmoves + function.entry.instructions - - for block in function.Blocks: - for stmt in block.instructions: - rewriteStmt(stmt, frame) - linearize(block) - # TODO: schedule here? - -# Visit all nodes with some function: -# TODO: rewrite into visitor. - -# Rewrite rewrites call instructions into Eseq instructions. - - -def rewriteStmt(stmt, frame): - if isinstance(stmt, ir.Jump): - pass - elif isinstance(stmt, ir.CJump): - stmt.a = rewriteExp(stmt.a, frame) - stmt.b = rewriteExp(stmt.b, frame) - elif isinstance(stmt, ir.Move): - stmt.src = rewriteExp(stmt.src, frame) - stmt.dst = rewriteExp(stmt.dst, frame) - elif isinstance(stmt, ir.Terminator): - pass - elif isinstance(stmt, ir.Exp): - stmt.e = rewriteExp(stmt.e, frame) - else: - raise NotImplementedError('STMT NI: {}'.format(stmt)) - -newTemp = irutils.NamedClassGenerator('canon_reg', ir.Temp).gen - -def rewriteExp(exp, frame): - if isinstance(exp, ir.Binop): - exp.a = rewriteExp(exp.a, frame) - exp.b = rewriteExp(exp.b, frame) - return exp - elif isinstance(exp, ir.Const): - return exp - elif isinstance(exp, ir.Temp): - return exp - elif isinstance(exp, ir.Parameter): - return frame.parMap[exp] - elif isinstance(exp, ir.LocalVariable): - offset = frame.allocVar(exp) - return ir.Add(frame.fp, ir.Const(offset)) - elif isinstance(exp, ir.Mem): - exp.e = rewriteExp(exp.e, frame) - return exp - elif isinstance(exp, ir.Call): - exp.arguments = [rewriteExp(p, frame) for p in exp.arguments] - # Rewrite call into eseq: - t = newTemp() - return ir.Eseq(ir.Move(t, exp), t) - else: - raise NotImplementedError('NI: {}'.format(exp)) - -# The flatten functions pull out seq instructions to the sequence list. - -def flattenExp(exp): - if isinstance(exp, ir.Binop): - exp.a, sa = flattenExp(exp.a) - exp.b, sb = flattenExp(exp.b) - return exp, sa + sb - elif isinstance(exp, ir.Temp): - return exp, [] - elif isinstance(exp, ir.Const): - return exp, [] - elif isinstance(exp, ir.Mem): - exp.e, s = flattenExp(exp.e) - return exp, s - elif isinstance(exp, ir.Eseq): - s = flattenStmt(exp.stmt) - exp.e, se = flattenExp(exp.e) - return exp.e, s + se - elif isinstance(exp, ir.Call): - sp = [] - p = [] - for p_, sp_ in (flattenExp(p) for p in exp.arguments): - p.append(p_) - sp.extend(sp_) - exp.arguments = p - return exp, sp - else: - raise NotImplementedError('NI: {}'.format(exp)) - - -def flattenStmt(stmt): - if isinstance(stmt, ir.Jump): - return [stmt] - elif isinstance(stmt, ir.CJump): - stmt.a, sa = flattenExp(stmt.a) - stmt.b, sb = flattenExp(stmt.b) - return sa + sb + [stmt] - elif isinstance(stmt, ir.Move): - stmt.dst, sd = flattenExp(stmt.dst) - stmt.src, ss = flattenExp(stmt.src) - return sd + ss + [stmt] - elif isinstance(stmt, ir.Terminator): - return [stmt] - elif isinstance(stmt, ir.Exp): - stmt.e, se = flattenExp(stmt.e) - return se + [stmt] - else: - raise NotImplementedError('STMT NI: {}'.format(stmt)) - - -def linearize(block): - """ - Move seq instructions to top and flatten these in an instruction list - """ - i = list(flattenStmt(s) for s in block.instructions) - block.instructions = list(chain.from_iterable(i))
--- a/python/ppci/codegen/codegen.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -from .. import ir -from ..irutils import Verifier -from ..target import Target -from .. import CompilerError -from .canon import make as canonicalize -from .registerallocator import RegisterAllocator -import logging - - -class CodeGenerator: - """ Generic code generator """ - def __init__(self, target): - # TODO: schedule traces in better order. - # This is optional! - assert isinstance(target, Target), target - self.logger = logging.getLogger('codegen') - self.target = target - self.ins_sel = target.ins_sel - self.ra = RegisterAllocator() - self.verifier = Verifier() - - def generateFunc(self, irfunc, outs): - """ Generate code for one function into a frame """ - self.logger.debug('Generating code for {}'.format(irfunc.name)) - # Create a frame for this function: - frame = self.target.FrameClass(ir.label_name(irfunc)) - - # Canonicalize the intermediate language: - canonicalize(irfunc, frame) - self.logger.debug('after canonicalize', extra={'irfunc': irfunc}) - self.verifier.verify_function(irfunc) - self.ins_sel.munchFunction(irfunc, frame) - self.logger.debug('Selected instructions', extra={'ppci_frame': frame}) - - # Do register allocation: - self.ra.allocFrame(frame) - self.logger.debug('Registers allocated, now adding final glue') - # TODO: Peep-hole here? - - # Add label and return and stack adjustment: - frame.EntryExitGlue3() - - # Materialize the register allocated instructions into a stream of - # real instructions. - self.target.lower_frame_to_stream(frame, outs) - self.logger.debug('Instructions materialized') - return frame - - def generate(self, ircode, outs): - """ Generate code into output stream """ - assert isinstance(ircode, ir.Module) - outs.selectSection('code') - - # Munch program into a bunch of frames. One frame per function. - # Each frame has a flat list of abstract instructions. - # Generate code for all functions: - self.frames = [self.generateFunc(f, outs) for f in ircode.Functions] - return self.frames
--- a/python/ppci/codegen/dag.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ - -# Instruction selection with DAG (Directed Acyclic Graph) -class DagLeaf: - def __init__(self, v): - self.v = v - -class DagNode: - def __init__(self, name): - self.name = name - self.children = [] - def __repr__(self): - return str(self.name) - -class Dag: - def __init__(self, bb): - self.mapping = {} - self.buildFromBB(bb) - - def buildFromBB(self, bb): - for ins in bb.Instructions: - if type(ins) is ir.BinaryOperator: - if not ins.value1 in self.mapping: - self.mapping[ins.value1] = DagNode(ins.value1) - if not ins.value2 in self.mapping: - self.mapping[ins.value2] = DagNode(ins.value2) - # look for op with left and right operand the same: - N = None - lnode = self.mapping[ins.value1] - rnode = self.mapping[ins.value2] - for node in self.mapping.values(): - if node.name == ins.operation: - if node.children[0] == lnode and node.children[1] == rnode: - N = node - break - if not N: - # Create a node. - N = DagNode(ins.operation) - N.children.append(lnode) - N.children.append(rnode) - self.mapping[ins.result] = N - else: - pass - - def dumpgv(self, outf): - outf.write('subgraph {0} {{\n'.format(id(self))) - for node in self.mapping.values(): - outf.write('{0} [label="{1}"];\n'.format(id(node), node.name)) - for c in node.children: - outf.write('{0} -> {1};\n'.format(id(node), id(c))) - outf.write('label="dag"}\n') - -def insSelect(mod): - """ Create DAG from ir-code """ - for bb in mod.BasicBlocks: - print(bb) - dag = Dag(bb) - print(dag.mapping) - bb.dag = dag -
--- a/python/ppci/codegen/flowgraph.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -from .graph import DiGraph, DiNode - - -class FlowGraphNode(DiNode): - """ A node in the flow graph """ - def __init__(self, g, ins): - super().__init__(g) - self.ins = ins - self.uses = set(ins.src) - self.defs = set(ins.dst) - self.live_in = set() - self.live_out = set() - - def __repr__(self): - r = '{}'.format(self.ins) - if self.uses: - r += ' uses:' + ', '.join(str(u) for u in self.uses) - if self.defs: - r += ' defs:' + ', '.join(str(d) for d in self.defs) - return r - - - -class FlowGraph(DiGraph): - def __init__(self, instrs): - """ Create a flowgraph from a list of abstract instructions """ - super().__init__() - self._map = {} - # Add nodes: - for ins in instrs: - n = FlowGraphNode(self, ins) - self._map[ins] = n - self.add_node(n) - - # Make edges: - prev = None - for ins in instrs: - n = self._map[ins] - if prev: - self.addEdge(prev, n) - if ins.jumps: - prev = None - for j in ins.jumps: - to_n = self._map[j] - self.addEdge(n, to_n) - else: - prev = n
--- a/python/ppci/codegen/graph.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,107 +0,0 @@ - -class Graph: - """ - Generic graph base class. - Can dump to graphiz dot format for example! - """ - def __init__(self): - self.nodes = set() - self.edges = set() - self.adj_map = {} - - def add_node(self, n): - self.nodes.add(n) - if n not in self.adj_map: - self.adj_map[n] = set() - - def delNode(self, n): - self.nodes.remove(n) - - def addEdge(self, n, m): - """ Add an edge from n to m """ - self.edges.add((n, m)) - self.edges.add((m, n)) - self.adj_map[n].add(m) - self.adj_map[m].add(n) - - def hasEdge(self, n, m): - return (n, m) in self.edges - - def delEdge(self, n, m): - self.edges.remove((n, m)) - self.edges.remove((m, n)) - - def adjecent(self, n): - """ Return all nodes with edges to n """ - return self.adj_map[n] & self.nodes - - def to_dot(self, f): - """ Generate graphviz dot representation """ - for n in self.nodes: - print(' {} [label="{}" shape=box3d];'.format(id(n), n), file=f) - for n, m in self.edges: - print(' {} -> {};'.format(id(n), id(m)), file=f) - - -class Node: - """ - Node in a graph. - """ - def __init__(self, g): - self.g = g - self.addDegree = 0 # Hack to increase degree - - @property - def Adjecent(self): - return self.g.adjecent(self) - - @property - def Degree(self): - return len(self.Adjecent) + self.addDegree - - -class DiGraph(Graph): - """ Directed graph. """ - def __init__(self): - super().__init__() - self.suc_map = {} - self.pre_map = {} - - def addEdge(self, n, m): - """ Add a directed edge from n to m """ - assert n in self.nodes - assert m in self.nodes - self.edges.add((n, m)) - self.suc_map[n].add(m) - self.pre_map[m].add(n) - self.adj_map[n].add(m) - self.adj_map[m].add(n) - - def add_node(self, n): - super().add_node(n) - if n not in self.suc_map: - self.suc_map[n] = set() - if n not in self.pre_map: - self.pre_map[n] = set() - - def hasEdge(self, n, m): - return (n, m) in self.edges - - def successors(self, n): - return self.suc_map[n] & self.nodes - - def predecessors(self, n): - return self.pre_map[n] & self.nodes - - -class DiNode(Node): - @property - def Succ(self): - return self.g.successors(self) - - @property - def Pred(self): - return self.g.predecessors(self) - - def __gt__(self, other): - return self in other.Succ
--- a/python/ppci/codegen/interferencegraph.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,100 +0,0 @@ -import logging -from .graph import Graph, Node - - -class InterferenceGraphNode(Node): - def __init__(self, g, varname): - super().__init__(g) - self.temps = [varname] - self.moves = set() - self.color = None - - def __repr__(self): - return '{}({})'.format(self.temps, self.color) - - -class InterferenceGraph(Graph): - """ - Interference graph. - """ - def __init__(self, flowgraph): - """ Create a new interference graph from a flowgraph """ - super().__init__() - self.logger = logging.getLogger('interferencegraph') - # Calculate liveness in CFG: - ### - # Liveness: - # in[n] = use[n] UNION (out[n] - def[n]) - # out[n] = for s in n.succ in union in[s] - ### - for n in flowgraph.nodes: - n.live_in = set() - n.live_out = set() - - # Sort flowgraph nodes backwards: - cfg_nodes = list(flowgraph.nodes) - self.logger.debug('CFG nodes: {}'.format(cfg_nodes)) - cfg_nodes.sort(reverse=True) - - # Dataflow fixed point iteration: - n_iterations = 0 - change = True - while change: - change = False - for n in cfg_nodes: - _in = n.live_in - _out = n.live_out - n.live_in = n.uses | (n.live_out - n.defs) - if n.Succ: - n.live_out = set.union(*(s.live_in for s in n.Succ)) - else: - n.live_out = set() - n.live_out = n.live_out | n.defs - change = change or (_in != n.live_in) or (_out != n.live_out) - n_iterations += 1 - - self.logger.debug('Iterations: {} * {}'.format(n_iterations, len(cfg_nodes))) - # Construct interference graph: - for n in flowgraph.nodes: - for tmp in n.live_out: - n1 = self.getNode(tmp) - for tmp2 in (n.live_out - {tmp}): - n2 = self.getNode(tmp2) - self.addEdge(n1, n2) - - def to_dot(self, f): - """ Generate graphviz dot representation """ - for n in self.nodes: - print(' {} [label="{}" shape=box3d];'.format(id(n), n), file=f) - for n, m in self.edges: - print(' {} -> {};'.format(id(n), id(m)), file=f) - - def to_txt(self): - for node in self.nodes: - print('{} interferes: {}'.format(node, node.Adjecent)) - - def getNode(self, tmp): - # Linear search - # TODO: can be improved for speed! - for n in self.nodes: - if tmp in n.temps: - return n - n = InterferenceGraphNode(self, tmp) - self.add_node(n) - return n - - def Combine(self, n, m): - """ Combine n and m into n """ - n.temps.extend(m.temps) - n.moves.update(m.moves) - # Reroute all edges: - e1 = [e for e in self.edges if e[0] is m] - e2 = [e for e in self.edges if e[1] is m] - for e in e1: - self.edges.remove(e) - self.addEdge(n, e[1]) - for e in e2: - self.edges.remove(e) - self.addEdge(n, e[0]) - # Remove node m: - self.delNode(m)
--- a/python/ppci/codegen/registerallocator.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,197 +0,0 @@ -import logging -from .flowgraph import FlowGraph -from .interferencegraph import InterferenceGraph - -# Nifty first function: -first = lambda x: next(iter(x)) - - -class RegisterAllocator: - """ - Target independent register allocator. - - Algorithm is iterated register coalescing by Appel and George. - - Chaitin's algorithm: remove all nodes with less than K neighbours. - These nodes can be colored when added back. - - The process consists of the following steps: - - build interference graph from the instruction list - - remove low degree non move related nodes. - - (optional) coalesc registers to remove redundant moves - - (optional) spill registers - - select registers - """ - def __init__(self): - self.logger = logging.getLogger('registerallocator') - - def InitData(self, f): - self.f = f - # Register information: - self.regs = set(f.regs) - self.K = len(self.regs) - - # Move related sets: - self.coalescedMoves = set() - self.constrainedMoves = set() - self.frozenMoves = set() - self.activeMoves = set() - self.worklistMoves = set() - - def Build(self): - """ 1. Construct interference graph from instruction list """ - self.f.cfg = FlowGraph(self.f.instructions) - self.logger.debug('Constructed flowgraph', extra={'ra_cfg':self.f.cfg}) - self.f.ig = InterferenceGraph(self.f.cfg) - self.logger.debug('Constructed interferencegraph', extra={'ra_ig':self.f.ig}) - - self.Node = self.f.ig.getNode - - # Divide nodes into pre-colored and initial: - pre_tmp = list(self.f.tempMap.keys()) - self.precolored = set(self.Node(tmp) for tmp in pre_tmp) - self.workSet = set(self.f.ig.nodes - self.precolored) - - for n in self.precolored: - n.addDegree = 100 + len(self.f.ig.nodes) + self.K - - # Initialize color map: - self.color = {} - for tmp, c in self.f.tempMap.items(): - self.color[self.Node(tmp)] = c - - self.moves = [i for i in self.f.instructions if i.ismove] - for mv in self.moves: - self.Node(mv.src[0]).moves.add(mv) - self.Node(mv.dst[0]).moves.add(mv) - - def NodeMoves(self, n): - return n.moves & (self.activeMoves | self.worklistMoves) - - def MoveRelated(self, n): - return bool(self.NodeMoves(n)) - - @property - def SpillWorkSet(self): - c = lambda n: n.Degree >= self.K - return set(filter(c, self.workSet)) - - @property - def FreezeWorkSet(self): - c = lambda n: n.Degree < self.K and self.MoveRelated(n) - return set(filter(c, self.workSet)) - - @property - def SimplifyWorkSet(self): - c = lambda n: n.Degree < self.K and not self.MoveRelated(n) - return set(filter(c, self.workSet)) - - def makeWorkList(self): - """ Divide initial nodes into worklists """ - self.selectStack = [] - - # Fill initial move set: - for m in self.moves: - self.worklistMoves.add(m) - - def Simplify(self): - """ 2. Remove nodes from the graph """ - n = first(self.SimplifyWorkSet) - self.workSet.remove(n) - self.selectStack.append(n) - # Pop out of graph: - self.f.ig.delNode(n) - - def EnableMoves(self, nodes): - for n in nodes: - for m in self.NodeMoves(n): - if m in self.activeMoves: - self.activeMoves.remove(m) - self.worklistMoves.add(m) - - def Coalesc(self): - """ Coalesc conservative. """ - m = first(self.worklistMoves) - x = self.Node(m.dst[0]) - y = self.Node(m.src[0]) - u, v = (y, x) if y in self.precolored else (x, y) - self.worklistMoves.remove(m) - if u is v: - self.coalescedMoves.add(m) - elif v in self.precolored or self.f.ig.hasEdge(u, v): - self.constrainedMoves.add(m) - elif u not in self.precolored and self.Conservative(u, v): - self.coalescedMoves.add(m) - self.workSet.remove(v) - self.f.ig.Combine(u, v) - else: - self.activeMoves.add(m) - - def Conservative(self, u, v): - """ Briggs conservative criteria for coalesc """ - nodes = u.Adjecent | v.Adjecent - c = lambda n: n.Degree >= self.K - k = len(list(filter(c, nodes))) - return k < self.K - - def Freeze(self): - """ Give up coalescing on some node """ - u = first(self.FreezeWorkSet) - self.freezeMoves(u) - - def freezeMoves(self, u): - """ Freeze moves for node u """ - for m in self.NodeMoves(u): - if m in self.activeMoves: - self.activeMoves.remove(m) - else: - sekf.worklistMoves.remove(m) - self.frozenMoves.add(m) - # Check other part of the move for still being move related: - v = m.src[0] if u is m.dst[0] else m.dst[0] - - def SelectSpill(self): - raise NotImplementedError("Spill is not implemented") - - def AssignColors(self): - """ Add nodes back to the graph to color it. """ - while self.selectStack: - n = self.selectStack.pop(-1) # Start with the last added - self.f.ig.add_node(n) - takenregs = set(self.color[m] for m in n.Adjecent) - okColors = self.regs - takenregs - if okColors: - self.color[n] = first(okColors) - n.color = self.color[n] - else: - raise NotImplementedError('Spill required here!') - - def ApplyColors(self): - # Remove coalesced moves: - for mv in self.coalescedMoves: - self.f.instructions.remove(mv) - - # Use allocated registers: - lookup = lambda t: self.color[self.Node(t)] - for i in self.f.instructions: - i.src = tuple(map(lookup, i.src)) - i.dst = tuple(map(lookup, i.dst)) - - def allocFrame(self, f): - """ Do iterated register allocation for a single stack frame. """ - self.InitData(f) - self.Build() - self.makeWorkList() - while True: - if self.SimplifyWorkSet: - self.Simplify() - elif self.worklistMoves: - self.Coalesc() - elif self.FreezeWorkSet: - self.Freeze() - elif self.SpillWorkSet: - raise NotImplementedError('Spill not implemented') - else: - break # Done! - self.AssignColors() - self.ApplyColors()
--- a/python/ppci/common.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -from collections import namedtuple -import logging - -""" - Error handling routines - Diagnostic utils - Source location structures -""" - -# Token is used in the lexical analyzer: -class Token: - def __init__(self, typ, val, loc=None): - self.typ = typ - self.val = val - if loc is None: - loc = SourceLocation('', 0, 0, 0) - assert type(loc) is SourceLocation - self.loc = loc - - def __repr__(self): - return 'Token({0}, {1})'.format(self.typ, self.val) - - -class SourceLocation: - def __init__(self, filename, row, col, ln): - self.filename = filename - self.row = row - self.col = col - self.length = ln - - def __repr__(self): - return '{}, {}, {}'.format(self.filename, self.row, self.col) - - -SourceRange = namedtuple('SourceRange', ['p1', 'p2']) - - -class CompilerError(Exception): - def __init__(self, msg, loc=None): - self.msg = msg - self.loc = loc - if loc: - assert type(loc) is SourceLocation, \ - '{0} must be SourceLocation'.format(type(loc)) - self.row = loc.row - self.col = loc.col - else: - self.row = self.col = 0 - - def __repr__(self): - return '"{}"'.format(self.msg) - - -class DiagnosticsManager: - def __init__(self): - self.diags = [] - self.sources = {} - self.logger = logging.getLogger('diagnostics') - - def addSource(self, name, src): - self.logger.debug('Adding source {}'.format(name)) - self.sources[name] = src - - def addDiag(self, d): - #self.logger.warning(str(d.msg)) - self.diags.append(d) - - def error(self, msg, loc): - self.addDiag(CompilerError(msg, loc)) - - def clear(self): - del self.diags[:] - self.sources.clear() - - def printErrors(self): - if len(self.diags) > 0: - print('{0} Errors'.format(len(self.diags))) - for d in self.diags: - self.printError(d) - - def printError(self, e): - def printLine(row, txt): - print(str(row)+':'+txt) - print('==============') - if not e.loc: - print('Error: {0}'.format(e)) - else: - if e.loc.filename not in self.sources: - print('Error: {0}'.format(e)) - return - print("File: {}".format(e.loc.filename)) - source = self.sources[e.loc.filename] - lines = source.split('\n') - ro, co = e.row, e.col - prerow = ro - 2 - if prerow < 1: - prerow = 1 - afterrow = ro + 3 - if afterrow > len(lines): - afterrow = len(lines) - - # print preceding source lines: - for r in range(prerow, ro): - printLine(r, lines[r-1]) - # print source line containing error: - printLine(ro, lines[ro-1]) - print(' '*(len(str(ro)+':')+co-1) + '^ Error: {0}'.format(e.msg)) - # print trailing source line: - for r in range(ro+1, afterrow+1): - printLine(r, lines[r-1]) - print('==============')
--- a/python/ppci/ir.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,383 +0,0 @@ -""" -Intermediate representation (IR) code classes. -""" - - -def label_name(dut): - """ Function that returns the assembly code label name """ - if isinstance(dut, Block): - f = dut.function - return label_name(f) + '_' + dut.name - elif isinstance(dut, Function): - return label_name(dut.module) + '_' + dut.name - elif isinstance(dut, Module): - return dut.name - else: - raise NotImplementedError(str(dut)) - - -class Module: - """ Container unit for variables and functions. """ - def __init__(self, name): - self.name = name - self.functions = [] - self.variables = [] - - def __repr__(self): - return 'module {0}'.format(self.name) - - def add_function(self, f): - """ Add a function to this module """ - self.functions.append(f) - f.module = self - - def add_variable(self, v): - self.variables.append(v) - - def getVariables(self): - return self.variables - - Variables = property(getVariables) - - def getFunctions(self): - return self.functions - - Functions = property(getFunctions) - - def findFunction(self, name): - for f in self.funcs: - if f.name == name: - return f - raise KeyError(name) - - getFunction = findFunction - - -class Function: - """ Represents a function. """ - def __init__(self, name, module=None): - self.name = name - self.entry = Block('entry') - self.entry.function = self - self.epiloog = Block('epilog') - self.epiloog.function = self - self.epiloog.addInstruction(Terminator()) - self.return_value = Temp('{}_retval'.format(name)) - self.arguments = [] - self.localvars = [] - if module: - module.add_function(self) - - def __repr__(self): - args = ','.join(str(a) for a in self.arguments) - return 'function i32 {}({})'.format(self.name, args) - - def add_block(self, bb): - #self.bbs.append(bb) - bb.function = self - - def removeBlock(self, bb): - #self.bbs.remove(bb) - bb.function = None - - def getBlocks(self): - bbs = [self.entry] - worklist = [self.entry] - while worklist: - b = worklist.pop() - for sb in b.Successors: - if sb not in bbs: - bbs.append(sb) - worklist.append(sb) - bbs.remove(self.entry) - if self.epiloog in bbs: - bbs.remove(self.epiloog) - bbs.insert(0, self.entry) - bbs.append(self.epiloog) - return bbs - - def findBasicBlock(self, name): - for bb in self.bbs: - if bb.name == name: - return bb - raise KeyError(name) - - Blocks = property(getBlocks) - - @property - def Entry(self): - return self.entry - - def check(self): - for b in self.Blocks: - b.check() - - def addParameter(self, p): - assert type(p) is Parameter - p.num = len(self.arguments) - self.arguments.append(p) - - def addLocal(self, l): - assert type(l) is LocalVariable - self.localvars.append(l) - - -class Block: - """ - Uninterrupted sequence of instructions with a label at the start. - """ - def __init__(self, name, function=None): - self.name = name - self.function = function - self.instructions = [] - - parent = property(lambda s: s.function) - - def __repr__(self): - return '{0}:'.format(self.name) - - def addInstruction(self, i): - i.parent = self - assert not isinstance(self.LastInstruction, LastStatement) - self.instructions.append(i) - - def replaceInstruction(self, i1, i2): - idx = self.instructions.index(i1) - i1.parent = None - i1.delete() - i2.parent = self - self.instructions[idx] = i2 - - def removeInstruction(self, i): - i.parent = None - #i.delete() - self.instructions.remove(i) - - @property - def Instructions(self): - return self.instructions - - @property - def LastInstruction(self): - if not self.Empty: - return self.instructions[-1] - - @property - def Empty(self): - return len(self.instructions) == 0 - - @property - def FirstInstruction(self): - return self.instructions[0] - - def getSuccessors(self): - if not self.Empty: - return self.LastInstruction.Targets - return [] - - Successors = property(getSuccessors) - - def getPredecessors(self): - preds = [] - for bb in self.parent.Blocks: - if self in bb.Successors: - preds.append(bb) - return preds - - Predecessors = property(getPredecessors) - - def precedes(self, other): - raise NotImplementedError() - - -# Instructions: - -class Expression: - """ Base class for an expression """ - pass - - -class Const(Expression): - """ Represents a constant value """ - def __init__(self, value): - self.value = value - - def __repr__(self): - return 'Const {}'.format(self.value) - - -class Call(Expression): - """ Call a function with some arguments """ - def __init__(self, f, arguments): - assert type(f) is str - self.f = f - self.arguments = arguments - - def __repr__(self): - args = ', '.join([str(arg) for arg in self.arguments]) - return '{}({})'.format(self.f, args) - - -# Data operations -class Binop(Expression): - """ Generic binary operation """ - ops = ['+', '-', '*', '/', '|', '&', '<<', '>>'] - - def __init__(self, value1, operation, value2): - assert operation in Binop.ops - self.a = value1 - self.b = value2 - self.operation = operation - - def __repr__(self): - a, b = self.a, self.b - return '({} {} {})'.format(a, self.operation, b) - - -def Add(a, b): - """ Add a and b """ - return Binop(a, '+', b) - - -def Sub(a, b): - """ Substract b from a """ - return Binop(a, '-', b) - - -def Mul(a, b): - """ Multiply a by b """ - return Binop(a, '*', b) - - -def Div(a, b): - """ Divide a in b pieces """ - return Binop(a, '/', b) - - -class Eseq(Expression): - """ Sequence of instructions where the last is an expression """ - def __init__(self, stmt, e): - self.stmt = stmt - self.e = e - - def __repr__(self): - return '({}, {})'.format(self.stmt, self.e) - - -class Alloc(Expression): - """ Allocates space on the stack """ - def __init__(self): - super().__init__() - - def __repr__(self): - return 'Alloc' - - -class Variable(Expression): - def __init__(self, name): - self.name = name - - def __repr__(self): - return 'Var {}'.format(self.name) - - -class LocalVariable(Variable): - def __repr__(self): - return 'Local {}'.format(self.name) - - -class Parameter(Variable): - def __repr__(self): - return 'Param {}'.format(self.name) - - -class Temp(Expression): - """ Temporary storage, same as register """ - def __init__(self, name): - self.name = name - - def __repr__(self): - return 'TMP_{}'.format(self.name) - - -class Mem(Expression): - """ Memory access """ - def __init__(self, e): - self.e = e - - def __repr__(self): - return '[{}]'.format(self.e) - - -class Statement: - """ Base class for all instructions. """ - @property - def IsTerminator(self): - return isinstance(self, LastStatement) - - -class Move(Statement): - """ Move source to destination """ - def __init__(self, dst, src): - self.dst = dst - self.src = src - - def __repr__(self): - return '{} = {}'.format(self.dst, self.src) - - -class Exp(Statement): - def __init__(self, e): - self.e = e - - def __repr__(self): - return '{}'.format(self.e) - - -# Branching: -class LastStatement(Statement): - def changeTarget(self, old, new): - idx = self.Targets.index(old) - self.Targets[idx] = new - - -class Terminator(LastStatement): - """ Instruction that terminates the terminal block """ - def __init__(self): - self.Targets = [] - - def __repr__(self): - return 'Terminator' - - -class Jump(LastStatement): - """ Jump statement to some target location """ - def __init__(self, target): - self.Targets = [target] - - def setTarget(self, t): - self.Targets[0] = t - - target = property(lambda s: s.Targets[0], setTarget) - - def __repr__(self): - return 'JUMP {}'.format(self.target.name) - - -class CJump(LastStatement): - """ Conditional jump to true or false labels. """ - conditions = ['==', '<', '>', '>=', '<=', '!='] - - def __init__(self, a, cond, b, lab_yes, lab_no): - assert cond in CJump.conditions - self.a = a - self.cond = cond - self.b = b - self.Targets = [lab_yes, lab_no] - - lab_yes = property(lambda s: s.Targets[0]) - lab_no = property(lambda s: s.Targets[1]) - - def __repr__(self): - return 'IF {} {} {} THEN {} ELSE {}'\ - .format(self.a, self.cond, self.b, self.lab_yes, self.lab_no)
--- a/python/ppci/ir2tree.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -from tree import Tree -from . import ir - -""" Create a tree from ir code. """ - -f_map = {} # Mapping from types to tree creation functions - -def register(tp): - """ Register a function for type tp """ - def reg_f(f): - f_map[tp] = f - return f - return reg_f - -@register(ir.Binop) -def binop_to_tree(e): - names = {'+':'ADDI32', '-':'SUBI32', '|':'ORI32', '<<':'SHLI32', - '*':'MULI32'} - op = names[e.operation] - return Tree(op, makeTree(e.a), makeTree(e.b)) - -@register(ir.Temp) -def temp_to_tree(e): - t = Tree('REGI32') - t.value = e - return t - -@register(ir.Const) -def const_to_tree(e): - t = Tree('CONSTI32') - t.value = e.value - return t - -@register(ir.Mem) -def mem_to_tree(e): - return Tree('MEMI32', makeTree(e.e)) - -@register(ir.Call) -def call_to_tree(e): - t = Tree('CALL') - t.value = e - return t - -def makeTree(ir_node): - """ Transform an ir node into a tree usable for matching """ - return f_map[type(ir_node)](ir_node)
--- a/python/ppci/irmach.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ - -""" - Abstract assembly language instructions. - - This is the second intermediate representation. - - Instructions are selected and scheduled at this stage. -""" - -from .target import Instruction - - -class Frame: - """ - Activation record abstraction. This class contains a flattened - function. Instructions are selected and scheduled at this stage. - Frames differ per machine. - """ - def __init__(self, name): - self.name = name - self.instructions = [] - self.stacksize = 0 - - def __repr__(self): - return 'Frame {}'.format(self.name) - - -class AbstractInstruction: - """ - Abstract machine instruction class. This is a very simple - abstraction of machine instructions. - """ - def __init__(self, cls, ops=(), src=(), dst=(), jumps=(), others=(), ismove=False): - assert type(cls) is type or isinstance(cls, Instruction), str(cls) - self.assem = cls - self.ops = tuple(ops) - self.src = tuple(src) - self.dst = tuple(dst) - self.jumps = tuple(jumps) - self.others = tuple(others) - self.ismove = ismove - - def __repr__(self): - """ Substitutes source, dst and labels in the string """ - if isinstance(self.assem, Instruction): - x = str(self.assem) - else: - cn = self.assem.__name__ - x = '{}, def={}, use={}, other={}' - x = x.format(cn, self.dst, self.src, self.others) - return x -
--- a/python/ppci/irutils.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,268 +0,0 @@ - -""" - Some utilities for ir-code. -""" -import re -from . import ir - -def dumpgv(m, outf): - print('digraph G ', file=outf) - print('{', file=outf) - for f in m.Functions: - print('{} [label="{}" shape=box3d]'.format(id(f), f), file=outf) - for bb in f.Blocks: - contents = str(bb) + '\n' - contents += '\n'.join([str(i) for i in bb.Instructions]) - print('{0} [shape=note label="{1}"];' - .format(id(bb), contents), file=outf) - for successor in bb.Successors: - print('"{}" -> "{}"'.format(id(bb), id(successor)), file=outf) - - print('"{}" -> "{}" [label="entry"]' - .format(id(f), id(f.entry)), file=outf) - print('}', file=outf) - - -class Writer: - def __init__(self, extra_indent=''): - self.extra_indent = extra_indent - - def write(self, ir, f): - """ Write ir-code to file f """ - print('{}{}'.format(self.extra_indent, ir), file=f) - for v in ir.Variables: - print('{}{}'.format(self.extra_indent, v), file=f) - for function in ir.Functions: - self.write_function(function, f) - - def write_function(self, fn, f): - args = ','.join('i32 ' + str(a) for a in fn.arguments) - print('{}function i32 {}({})'.format(self.extra_indent, fn.name, args), file=f) - for bb in fn.Blocks: - print('{} {}'.format(self.extra_indent, bb), file=f) - for ins in bb.Instructions: - print('{} {}'.format(self.extra_indent, ins), file=f) - - -class IrParseException(Exception): - pass - - -class Reader: - def read(self, f): - """ Read ir code from file f """ - # Read lines from the file: - lines = [line.rstrip() for line in f] - - # Create a regular expression for the lexing part: - tok_spec = [ - ('NUMBER', r'\d+'), - ('ID', r'[A-Za-z][A-Za-z\d_]*'), - ('SKIP2', r' '), - ('SKIP1', r' '), - ('OTHER', r'[\.,=:;\-+*\[\]/\(\)]|>|<|{|}|&|\^|\|') - ] - tok_re = '|'.join('(?P<%s>%s)' % pair for pair in tok_spec) - gettok = re.compile(tok_re).match - - def tokenize(): - for line in lines: - if not line: - continue # Skip empty lines - mo = gettok(line) - first = True - while mo: - typ = mo.lastgroup - val = mo.group(typ) - if typ == 'ID': - if val in ['function', 'module']: - typ = val - yield (typ, val) - elif typ == 'OTHER': - typ = val - yield (typ, val) - elif typ in ['SKIP1', 'SKIP2']: - if first: - yield (typ, val) - elif typ == 'NUMBER': - yield (typ, int(val)) - else: - raise NotImplementedError(str(typ)) - first = False - pos = mo.end() - mo = gettok(line, pos) - if len(line) != pos: - raise IrParseException('Lex fault') - yield ('eol', 'eol') - yield ('eof', 'eof') - self.tokens = tokenize() - self.token = self.tokens.__next__() - - try: - module = self.parse_module() - return module - except IrParseException as e: - print(e) - - def next_token(self): - t = self.token - if t[0] != 'eof': - self.token = self.tokens.__next__() - return t - - @property - def Peak(self): - return self.token[0] - - def Consume(self, typ): - if self.Peak == typ: - return self.next_token() - else: - raise IrParseException('Expected "{}" got "{}"'.format(typ, self.Peak)) - - def parse_module(self): - """ Entry for recursive descent parser """ - self.Consume('module') - name = self.Consume('ID')[1] - module = ir.Module(name) - self.Consume('eol') - while self.Peak != 'eof': - if self.Peak == 'function': - module.add_function(self.parse_function()) - else: - raise IrParseException('Expected function got {}'.format(self.Peak)) - return module - - def parse_function(self): - self.Consume('function') - self.parse_type() - name = self.Consume('ID')[1] - function = ir.Function(name) - self.Consume('(') - while self.Peak != ')': - self.parse_type() - self.Consume('ID') - if self.Peak != ',': - break - else: - self.Consume(',') - self.Consume(')') - self.Consume('eol') - while self.Peak == 'SKIP1': - function.add_block(self.parse_block()) - return function - - def parse_type(self): - self.Consume('ID') - - def parse_block(self): - self.Consume('SKIP1') - name = self.Consume('ID')[1] - block = ir.Block(name) - self.Consume(':') - self.Consume('eol') - while self.Peak == 'SKIP2': - self.parse_statement() - return block - - def parse_statement(self): - self.Consume('SKIP2') - while self.Peak != 'eol': - # raise NotImplementedError() - self.next_token() - self.Consume('eol') - - -# Constructing IR: - -class NamedClassGenerator: - def __init__(self, prefix, cls): - self.prefix = prefix - self.cls = cls - - def NumGen(): - a = 0 - while True: - yield a - a = a + 1 - self.nums = NumGen() - - def gen(self, prefix=None): - if not prefix: - prefix = self.prefix - return self.cls('{0}{1}'.format(prefix, self.nums.__next__())) - - -class Builder: - """ Base class for ir code generators """ - def __init__(self): - self.prepare() - - def prepare(self): - self.newTemp = NamedClassGenerator('reg', ir.Temp).gen - self.newBlock2 = NamedClassGenerator('block', ir.Block).gen - self.bb = None - self.m = None - self.fn = None - self.loc = None - - # Helpers: - def setModule(self, m): - self.m = m - - def newFunction(self, name): - f = ir.Function(name) - self.m.add_function(f) - return f - - def newBlock(self): - assert self.fn - b = self.newBlock2() - b.function = self.fn - return b - - def setFunction(self, f): - self.fn = f - self.bb = f.entry if f else None - - def setBlock(self, b): - self.bb = b - - def setLoc(self, l): - self.loc = l - - def emit(self, i): - assert isinstance(i, ir.Statement) - i.debugLoc = self.loc - if not self.bb: - raise Exception('No basic block') - self.bb.addInstruction(i) - - -class Verifier: - def verify(self, module): - """ Verifies a module for some sanity """ - assert isinstance(module, ir.Module) - for f in module.Functions: - self.verify_function(f) - - def verify_function(self, function): - for b in function.Blocks: - self.verify_block_termination(b) - - # Now we can build a dominator tree - for b in function.Blocks: - self.verify_block(b) - - def verify_block_termination(self, block): - assert not block.Empty - assert block.LastInstruction.IsTerminator - for i in block.Instructions[:-1]: - assert not isinstance(i, ir.LastStatement) - - def verify_block(self, block): - for instruction in block.Instructions: - self.verify_instruction(instruction) - - def verify_instruction(self, instruction): - pass
--- a/python/ppci/linker.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,147 +0,0 @@ - -import struct -from .objectfile import ObjectFile -from . import CompilerError - -def align(x, m): - while ((x % m) != 0): - x = x + 1 - return x - -def wrap_negative(x, bits): - b = struct.unpack('<I', struct.pack('<i', x))[0] - mask = (1 << bits) - 1 - return b & mask - -reloc_map = {} - -def reloc(t): - def f(c): - reloc_map[t] = c - return f - - -@reloc('lit_add_8') -def apply_lit8(reloc, sym, section, reloc_value): - assert sym.value % 4 == 0 - offset = (sym.value - (align(reloc_value + 2, 4))) - assert offset in range(0, 1024, 4), str(offset)+str( self.dst.sections) - rel8 = offset >> 2 - section.data[reloc.offset] = rel8 - - -@reloc('wrap_new11') -def apply_wrap_new11(reloc, sym, section, reloc_value): - offset = sym.value - (align(reloc_value, 2) + 4) - assert offset in range(-2048, 2046, 2) - imm11 = wrap_negative(offset >> 1, 11) - section.data[reloc.offset] = (imm11 & 0xff) - section.data[reloc.offset + 1] |= (imm11 >> 8) & 0x7 - - -@reloc('rel8') -def apply_rel8(reloc, sym, section, reloc_value): - assert sym.value % 2 == 0 - offset = sym.value - (align(reloc_value, 2) + 4) - assert offset in range(-256, 254, 2), str(offset) + str(reloc) - imm8 = wrap_negative(offset >> 1, 8) - section.data[reloc.offset] = imm8 - - -@reloc('bl_imm11_imm10') -def apply_bl_imm11(reloc, sym, section, reloc_value): - assert sym.value % 2 == 0 - offset = sym.value - (align(reloc_value, 2) + 4) - assert offset in range(-16777216, 16777214, 2), str(offset) - imm32 = wrap_negative(offset >> 1, 32) - imm11 = imm32 & 0x7FF - imm10 = (imm32 >> 11) & 0x3FF - s = (imm32 >> 24) & 0x1 - section.data[reloc.offset + 2] = imm11 & 0xFF - section.data[reloc.offset + 3] |= (imm11 >> 8) & 0x7 - section.data[reloc.offset] = imm10 & 0xff - section.data[reloc.offset + 1] |= ((imm10 >> 8) & 0x3) | (s << 2) - -@reloc('b_imm11_imm6') -def apply_b_imm11_imm6(reloc, sym, section, reloc_value): - assert sym.value % 2 == 0 - offset = sym.value - (align(reloc_value, 2) + 4) - assert offset in range(-1048576, 1048574, 2), str(offset) - imm32 = wrap_negative(offset >> 1, 32) - imm11 = imm32 & 0x7FF - imm6 = (imm32 >> 11) & 0x3F - s = (imm32 >> 24) & 0x1 - section.data[reloc.offset + 2] = imm11 & 0xFF - section.data[reloc.offset + 3] |= (imm11 >> 8) & 0x7 - section.data[reloc.offset] |= imm6 - section.data[reloc.offset + 1] |= (s << 2) - -# ARM reloc!! -# TODO: move to target classes??? -@reloc('b_imm24') -def apply_b_imm24(reloc, sym, section, reloc_value): - assert sym.value % 4 == 0 - assert reloc_value % 4 == 0 - offset = (sym.value - (reloc_value + 8)) - rel24 = wrap_negative(offset >> 2, 24) - section.data[reloc.offset+2] = (rel24 >> 16) & 0xFF - section.data[reloc.offset+1] = (rel24 >> 8) & 0xFF - section.data[reloc.offset+0] = rel24 & 0xFF - - -class Linker: - """ Merges the sections of several object files and - performs relocation """ - def link(self, objs, layout={}): - # Create new object file to store output: - self.dst = ObjectFile() - - # Create sections with address: - for section_name, address in layout.items(): - self.dst.get_section(section_name).address = address - - # First copy all sections into output sections: - for iobj in objs: - offsets = {} - # Merge sections: - for in_s in iobj.sections.values(): - out_s = self.dst.get_section(in_s.name) - # TODO: align section in other way: - while out_s.Size % 4 != 0: - out_s.add_data(bytes([0])) - - # Add new section: - offsets[in_s.name] = out_s.Size - out_s.add_data(in_s.data) - - - # Merge symbols: - for sym in iobj.symbols.values(): - out_s = self.dst.get_section(sym.section) - value = offsets[sym.section] + out_s.address + sym.value - self.dst.add_symbol(sym.name, value, sym.section) - - # Merge relocations: - for reloc in iobj.relocations: - offset = offsets[reloc.section] + reloc.offset - self.dst.add_relocation(reloc.sym, offset, reloc.typ, reloc.section) - - # Perform relocations: - for reloc in self.dst.relocations: - # Lookup symbol: - if reloc.sym not in self.dst.symbols: - raise CompilerError('Undefined reference "{}"'.format(reloc.sym)) - sym = self.dst.symbols[reloc.sym] - # patch up: - section = self.dst.get_section(reloc.section) - - # Determine location in memory of reloc patchup position: - reloc_value = section.address + reloc.offset - - if reloc.typ in reloc_map: - f = reloc_map[reloc.typ] - f(reloc, sym, section, reloc_value) - else: - raise NotImplementedError('Unknown relocation type {}'.format(reloc.typ)) - - return self.dst
--- a/python/ppci/mem2reg.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -import logging -from transform import FunctionPass -from ir import * - -def isAllocPromotable(allocinst): - # Check if alloc value is only used by load and store operations. - assert type(allocinst) is Alloc - return all(type(use) in [Load, Store] for use in allocinst.value.used_by) - - -class Mem2RegPromotor(FunctionPass): - def promoteSingleBlock(self, ai): - v = ai.value - bb = ai.Block - - # Replace all loads with the value: - loads = [i for i in v.used_by if isinstance(i, Load)] - stores = [i for i in v.used_by if isinstance(i, Store)] - stores.sort(key=lambda s: s.Position) - stores.reverse() - - for load in loads: - idx = load.Position - # Search upwards: - for store in stores: - if store.Position < load.Position: - break - load.value.replaceby(store.value) - logging.debug('replaced {} with {}'.format(load, store.value)) - bb.removeInstruction(load) - - # Remove store instructions: - for store in stores: - sv = store.value - logging.debug('removing {}'.format(store)) - bb.removeInstruction(store) - #assert sv.Used - - # Remove alloca instruction: - assert not ai.value.Used, ai.value.used_by - bb.removeInstruction(ai) - - def promote(self, ai): - # Find load operations and replace them with assignments - v = ai.value - if len(ai.value.UsedInBlocks) == 1: - self.promoteSingleBlock(ai) - return - - loads = [i for i in v.used_by if isinstance(i, Load)] - stores = [i for i in v.used_by if isinstance(i, Store)] - - # Each store instruction can be removed (later). - # Instead of storing the value, we use it - # where the load would have been! - replMap = {} - for store in stores: - replMap[store] = store.value - - # for each load, track back what the defining store - # was. - for load in loads: - pass - - def onFunction(self, f): - for bb in f.BasicBlocks: - allocs = [i for i in bb.Instructions if isinstance(i, Alloc)] - for i in allocs: - if isAllocPromotable(i): - self.promote(i)
--- a/python/ppci/objectfile.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ - -""" -Object files are used to store assembled code. Information contained -is code, symbol table and relocation information. -""" - -from . import CompilerError - -class Symbol: - def __init__(self, name, value, section): - self.name = name - self.value = value - self.section = section - - def __repr__(self): - return 'SYM {}, val={} sec={}'.format(self.name, self.value, self.section) - - -class Relocation: - """ Represents a relocation entry. A relocation always has a symbol to refer to - and a relocation type """ - def __init__(self, sym, offset, typ, section): - self.sym = sym - self.offset = offset - self.typ = typ - self.section = section - - def __repr__(self): - return 'RELOC {} off={} t={} sec={}'.format(self.sym, self.offset, self.typ, self.section) - - -class Section: - def __init__(self, name): - self.name = name - self.address = 0 - self.data = bytearray() - - def add_data(self, data): - self.data += data - - @property - def Size(self): - return len(self.data) - - def __repr__(self): - return 'SECTION {}'.format(self.name) - - -class ObjectFile: - """ Container for sections with compiled code or data. - Also contains symbols and relocation entries """ - def __init__(self): - self.symbols = {} - self.sections = {} - self.relocations = [] - - def find_symbol(self, name): - return self.symbols[name] - - def add_symbol(self, name, value, section): - if name in self.symbols: - raise CompilerError('{} already defined'.format(name)) - assert section in self.sections - sym = Symbol(name, value, section) - self.symbols[name] = sym - return sym - - def add_relocation(self, sym_name, offset, typ, section): - assert type(sym_name) is str, str(sym_name) - assert section in self.sections - reloc = Relocation(sym_name, offset, typ, section) - self.relocations.append(reloc) - return reloc - - def get_section(self, name): - if not name in self.sections: - self.sections[name] = Section(name) - return self.sections[name]
--- a/python/ppci/optimize.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -from mem2reg import Mem2RegPromotor -from transform import CommonSubexpressionElimination, CleanPass -from transform import DeadCodeDeleter, ConstantFolder - -def optimize(ir): - return - cf = ConstantFolder() - cf.run(ir) - return - dcd = DeadCodeDeleter() - m2r = Mem2RegPromotor() - clr = CleanPass() - cse = CommonSubexpressionElimination() - dcd.run(ir) - clr.run(ir) - m2r.run(ir) - cse.run(ir) - cf.run(ir) - dcd.run(ir)
--- a/python/ppci/outstream.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -import binascii -from ppci.target import Instruction, Alignment -from ppci.objectfile import ObjectFile - -""" - The output stream is a stream of instructions that can be output - to a file or binary or hexfile. -""" - - -class OutputStream: - def emit(self, item): - raise NotImplementedError('Abstract base class') - - def selectSection(self, sname): - raise NotImplementedError('Abstract base class') - - -class OutputStreamWriter: - def __init__(self, extra_indent=''): - self.extra_indent = extra_indent - - def dump(self, stream, f): - for s in sorted(stream.sections.keys()): - # print('.section '+ s) - self.dumpSection(stream.sections[s], f) - - def dumpSection(self, s, f): - for i in s.instructions: - addr = i.address - insword = i.encode() - assert type(insword) is bytes - insword = binascii.hexlify(bytes(reversed(insword))).decode('ascii') - asm = str(i) - if len(insword) == 0: - print(' {}'.format(asm), file=f) - else: - print(' 0x{0:08x} 0x{1} {2}'.format(addr, insword, asm), file=f) - - -class BinaryOutputStream(OutputStream): - """ Output stream that writes to object file """ - def __init__(self, obj_file): - super().__init__() - self.obj_file = obj_file - - def emit(self, item): - """ Encode instruction and add symbol and relocation information """ - assert isinstance(item, Instruction), str(item) + str(type(item)) - assert self.currentSection - section = self.currentSection - address = self.currentSection.Size - b = item.encode() - syms = item.symbols() - relocs = item.relocations() - section.add_data(b) - for sym in syms: - self.obj_file.add_symbol(sym, address, section.name) - for sym, typ in relocs: - self.obj_file.add_relocation(sym, address, typ, section.name) - # Special case for align, TODO do this different? - if type(item) is Alignment: - while section.Size % item.align != 0: - section.add_data(bytes([0])) - - def selectSection(self, sname): - self.currentSection = self.obj_file.get_section(sname) - - -class DummyOutputStream(OutputStream): - def emit(self, item): - pass - - def selectSection(self, sname): - pass
--- a/python/ppci/recipe.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -import os -import yaml - -from .buildtasks import Compile, Assemble, Link -from .objectfile import ObjectFile -from .target.target_list import target_list - - -targets = {t.name: t for t in target_list} -targetnames = list(targets.keys()) - -class RecipeLoader: - """ Loads a recipe into a runner from a dictionary or file """ - def __init__(self): - self.directive_handlers = {} - for a in dir(self): - if a.startswith('handle_'): - f = getattr(self, a) - self.directive_handlers[a[7:]] = f - - def load_file(self, recipe_file, runner): - """ Loads a recipe dictionary into a task runner """ - self.recipe_dir = os.path.abspath(os.path.dirname(recipe_file)) - with open(recipe_file, 'r') as f: - recipe = yaml.load(f) - self.runner = runner - self.load_dict(recipe) - - def relpath(self, filename): - return os.path.join(self.recipe_dir, filename) - - def openfile(self, filename): - return open(self.relpath(filename), 'r') - - def handle_compile(self, value): - sources = [self.openfile(s) for s in value['sources']] - includes = [self.openfile(i) for i in value['includes']] - target = targets[value['machine']] - output = ObjectFile() - task = Compile(sources, includes, target, output) - self.runner.add_task(task) - return task - - def handle_assemble(self, value): - asm_src = self.openfile(value['source']) - target = targets[value['machine']] - output = ObjectFile() - task = Assemble(asm_src, target, output) - self.runner.add_task(task) - return task - - def handle_link(self, value): - inputs = value['inputs'] - objs = [] - for i in inputs: - task = self.load_dict(i) - objs.append(task.output) - layout = value['layout'] - output = self.relpath(value['output']) - self.runner.add_task(Link(objs, layout, output)) - - def handle_apps(self, value): - for a in value: - self.load_dict(a) - - def load_dict(self, recipe): - for command, value in recipe.items(): - return self.directive_handlers[command](value) - - -
--- a/python/ppci/report.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ - -import logging -import io - -from . import outstream -from .c3 import AstPrinter -from . import logformat -from .irutils import Writer - -class RstFormatter(logging.Formatter): - """ Formatter that tries to create an rst document """ - def __init__(self): - super().__init__(fmt=logformat) - - def format(self, record): - s = super().format(record) - s += '\n' - if hasattr(record, 'c3_ast'): - f = io.StringIO() - print('', file=f) - print('', file=f) - print('.. code::', file=f) - print('', file=f) - AstPrinter().printAst(record.c3_ast, f) - print('', file=f) - s += '\n' + f.getvalue() - if hasattr(record, 'ircode'): - f = io.StringIO() - print('', file=f) - print('', file=f) - print('.. code::', file=f) - print('', file=f) - Writer(' ').write(record.ircode, f) - print('', file=f) - s += '\n' + f.getvalue() - if hasattr(record, 'irfunc'): - f = io.StringIO() - print('', file=f) - print('', file=f) - print('.. code::', file=f) - print('', file=f) - Writer(' ').write_function(record.irfunc, f) - print('', file=f) - s += '\n' + f.getvalue() - if hasattr(record, 'ppci_frame'): - f = io.StringIO() - frame = record.ppci_frame - print('', file=f) - print('.. code::', file=f) - print('', file=f) - print(' {}'.format(frame.name), file=f) - for i in frame.instructions: - print(' {}'.format(i),file=f) - print('', file=f) - s += '\n' + f.getvalue() - if hasattr(record, 'ra_cfg'): - f = io.StringIO() - print('', file=f) - print('', file=f) - print('.. graphviz::', file=f) - print('', file=f) - print(' digraph G {', file=f) - print(' size="8,80";', file=f) - cfg = record.ra_cfg - cfg.to_dot(f) - print(' }', file=f) - print('', file=f) - s += '\n' + f.getvalue() - if hasattr(record, 'ra_ig'): - f = io.StringIO() - print('', file=f) - print('', file=f) - print('.. graphviz::', file=f) - print('', file=f) - print(' digraph G {', file=f) - print(' ratio="compress";', file=f) - print(' size="8,80";', file=f) - ig = record.ra_ig - ig.to_dot(f) - print(' }', file=f) - print('', file=f) - s += '\n' + f.getvalue() - if hasattr(record, 'zcc_outs'): - f = io.StringIO() - print('', file=f) - print('', file=f) - print('.. code::', file=f) - print('', file=f) - outstream.OutputStreamWriter(' ').dump(record.zcc_outs, f) - print('', file=f) - s += '\n' + f.getvalue() - return s -
--- a/python/ppci/target/__init__.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -#!/usr/bin/env python - -from .basetarget import Nop, Instruction, Label, Target, Comment, Alignment - - -class SimpleTarget(Target): - def __init__(self): - super().__init__('SimpleTarget')
--- a/python/ppci/target/arm/__init__.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ - -from ..basetarget import Target -from ..arm.registers import R0, R1, R2, R3, R4, R5, R6, R7 -from ..arm.registers import R8, R9, R10, R11, R12, SP, LR, PC -from ..arm.registers import register_range - -from .instructions import Dcd, Mov, Add, Sub, Orr1, Mul, Mov2 -from .instructions import B, Bl, Ble, Bgt, Beq, Blt -from .instructions import Push, Pop, Str, Ldr, Ldr3, Str1, Ldr1 -from .selector import ArmInstructionSelector -from .frame import ArmFrame - -class ArmTarget(Target): - def __init__(self): - super().__init__('arm') - self.make_parser() - self.ins_sel = ArmInstructionSelector() - self.FrameClass = ArmFrame - - self.add_lowering(Ldr3, lambda im: Ldr3(im.dst[0], im.others[0])) - self.add_lowering(Str1, lambda im: Str1(im.src[1], im.src[0], im.others[0])) - self.add_lowering(Ldr1, lambda im: Ldr1(im.dst[0], im.src[0], im.others[0])) - self.add_lowering(Mov2, lambda im: Mov2(im.dst[0], im.src[0])) - - def make_parser(self): - # Assembly grammar: - self.add_keyword('r0') - self.add_keyword('r1') - self.add_keyword('r2') - self.add_keyword('r3') - self.add_keyword('r4') - self.add_keyword('r5') - self.add_keyword('r6') - self.add_keyword('r7') - self.add_keyword('r8') - self.add_keyword('r9') - self.add_keyword('r10') - self.add_keyword('r11') - self.add_keyword('r12') - self.add_keyword('sp') - self.add_keyword('lr') - self.add_keyword('pc') - - self.add_rule('reg', ['r0'], lambda rhs: R0) - self.add_rule('reg', ['r1'], lambda rhs: R1) - self.add_rule('reg', ['r2'], lambda rhs: R2) - self.add_rule('reg', ['r3'], lambda rhs: R3) - self.add_rule('reg', ['r4'], lambda rhs: R4) - self.add_rule('reg', ['r5'], lambda rhs: R5) - self.add_rule('reg', ['r6'], lambda rhs: R6) - self.add_rule('reg', ['r7'], lambda rhs: R7) - self.add_rule('reg', ['r8'], lambda rhs: R8) - self.add_rule('reg', ['r9'], lambda rhs: R9) - self.add_rule('reg', ['r10'], lambda rhs: R10) - self.add_rule('reg', ['r11'], lambda rhs: R11) - self.add_rule('reg', ['r12'], lambda rhs: R12) - self.add_rule('reg', ['sp'], lambda rhs: SP) - self.add_rule('reg', ['lr'], lambda rhs: LR) - self.add_rule('reg', ['pc'], lambda rhs: PC) - - self.add_keyword('dcd') - self.add_instruction(['dcd', 'imm32'], - lambda rhs: Dcd(rhs[1])) - - self.add_keyword('mov') - self.add_instruction(['mov', 'reg', ',', 'imm32'], - lambda rhs: Mov(rhs[1], rhs[3])) - - self.add_keyword('add') - self.add_instruction(['add', 'reg', ',', 'reg', ',', 'imm32'], - lambda rhs: Add(rhs[1], rhs[3], rhs[5])) - - self.add_instruction(['add', 'reg', ',', 'reg', ',', 'reg'], - lambda rhs: Add(rhs[1], rhs[3], rhs[5])) - - self.add_keyword('sub') - self.add_instruction(['sub', 'reg', ',', 'reg', ',', 'imm32'], - lambda rhs: Sub(rhs[1], rhs[3], rhs[5])) - - self.add_instruction(['sub', 'reg', ',', 'reg', ',', 'reg'], - lambda rhs: Sub(rhs[1], rhs[3], rhs[5])) - - self.add_keyword('mul') - self.add_instruction(['mul', 'reg', ',', 'reg', ',', 'reg'], - lambda rhs: Mul(rhs[1], rhs[3], rhs[5])) - - self.add_keyword('orr') - self.add_instruction(['orr', 'reg', ',', 'reg', ',', 'reg'], - lambda rhs: Orr1(rhs[1], rhs[3], rhs[5])) - - - # Jumping: - self.add_keyword('b') - self.add_instruction(['b', 'ID'], lambda rhs: B(rhs[1].val)) - self.add_keyword('ble') - self.add_instruction(['ble', 'ID'], lambda rhs: Ble(rhs[1].val)) - self.add_keyword('bgt') - self.add_instruction(['bgt', 'ID'], lambda rhs: Bgt(rhs[1].val)) - self.add_keyword('beq') - self.add_instruction(['beq', 'ID'], lambda rhs: Beq(rhs[1].val)) - self.add_keyword('blt') - self.add_instruction(['blt', 'ID'], lambda rhs: Blt(rhs[1].val)) - - self.add_keyword('bl') - self.add_instruction(['bl', 'ID'], lambda rhs: Bl(rhs[1].val)) - - # memory: - self.add_keyword('pop') - self.add_instruction(['pop', 'reg_list'], lambda rhs: Pop(rhs[1])) - - self.add_keyword('push') - self.add_instruction(['push', 'reg_list'], lambda rhs: Push(rhs[1])) - - self.add_keyword('ldr') - self.add_instruction(['ldr', 'reg', ',', '[', 'reg', ',', 'imm8', ']'], - lambda rhs: Ldr(rhs[1], rhs[4], rhs[6])) - - self.add_keyword('str') - self.add_instruction(['str', 'reg', ',', '[', 'reg', ',', 'imm8', ']'], - lambda rhs: Str(rhs[1], rhs[4], rhs[6])) - - self.add_instruction(['str', 'reg', ',', '[', 'reg', ',', 'reg', ']'], - lambda rhs: Str(rhs[1], rhs[4], rhs[6])) - - # Register list grammar: - self.add_rule('reg_list', ['{', 'reg_list_inner', '}'], - lambda rhs: rhs[1]) - self.add_rule('reg_list_inner', ['reg_or_range'], - lambda rhs: rhs[0]) - self.add_rule('reg_list_inner', ['reg_or_range', ',', 'reg_list_inner'], - lambda rhs: rhs[0] | rhs[2]) - self.add_rule('reg_or_range', ['reg'], lambda rhs: {rhs[0]}) - - self.add_rule('reg_or_range', ['reg', '-', 'reg'], - lambda rhs: register_range(rhs[0], rhs[2]))
--- a/python/ppci/target/arm/arm.brg Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ - -from ppci.target.arm.instructions import Add1, Sub1, Ldr1, Ldr3 - -%% - -%terminal ADDI32 SUBI32 MULI32 -%terminal ORI32 SHLI32 -%terminal CONSTI32 MEMI32 REGI32 CALL -%terminal MOVI32 - -%% - -reg: ADDI32(reg, reg) 2 (. d = self.newTmp(); self.emit(Add1, dst=[d], src=[$1, $2]); return d .) -reg: SUBI32(reg, reg) 2 (. d = self.newTmp(); self.emit(Sub1, dst=[d], src=[$1, $2]); return d .) -reg: SUBI32(reg, reg) 2 (. d = self.newTmp(); self.emit(Sub1, dst=[d], src=[$1, $2]); return d .) -reg: MEMI32(ADDI32(reg, cn)) 2 (. d = self.newTmp(); self.emit(Ldr1, dst=[d], src=[$1], others=[$2]); return d .) - - -cn: CONSTI32 0 (. return $$.value .) - -reg: CONSTI32 3 (. d = self.newTmp(); ln = self.selector.frame.addConstant($$.value); self.emit(Ldr3, dst=[d], others=[ln]); return d .) -reg: REGI32 1 (. return $$.value .)
--- a/python/ppci/target/arm/armv7.lidl Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ - -# This file specifies the encoding of the arm instruction set. - -fields { - word16 16 { - opcode 15:12 - top2 15:14 - top6 15:10 - data_opcode 9..6 - opB 11:9 - Rm 8:6 - Rn 5:3 - Rt 2:0 - } -} - -patterns { - add = 0 - sub, mul = 1..2 - [r1, r2, r3, r4, r5] is todo = 1..5 - [STR, STRH, STRB, LDRSB, LDR, LDRH, LDRB, LDRSH] is opcode = 0b0101 & opB = {0 to 7} - - EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL = 0..14 - [AND, EOR, LSL, LSR, ASR, ADC, SBC, ROR, TST, RSB, CMP, CMN, ORR, MUL, BIC, MVN] is 0..15 - - memop is STR | STRH | STRB | LDRSB | LDR | LDR | LDRH | LDRB | LDRSH -} - - -constructors -{ - alu rs1, reg_or_imm, rd - memop Rt, [Rn, Rm] is memop & Rt & Rn & Rm -} - -
--- a/python/ppci/target/arm/frame.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -from ... import ir -from ..basetarget import Label, Alignment -from ...irmach import AbstractInstruction, Frame -from .instructions import Dcd, Add, Sub, Push, Pop, Mov -from .registers import R0, R1, R2, R3, R4, R5, R6, R7, R8, R11, LR, PC, SP - - -class ArmFrame(Frame): - """ Arm specific frame for functions. """ - def __init__(self, name): - # We use r7 as frame pointer. - super().__init__(name) - self.regs = [R0, R1, R2, R3, R4, R5, R6, R7, R8] - self.rv = ir.Temp('special_RV') - self.p1 = ir.Temp('special_P1') - self.p2 = ir.Temp('special_P2') - self.p3 = ir.Temp('special_P3') - self.p4 = ir.Temp('special_P4') - self.fp = ir.Temp('special_FP') - # Pre-colored registers: - self.tempMap = {} - self.tempMap[self.rv] = R0 - self.tempMap[self.p1] = R1 - self.tempMap[self.p2] = R2 - self.tempMap[self.p3] = R3 - self.tempMap[self.p4] = R4 - self.tempMap[self.fp] = R11 - self.locVars = {} - self.parMap = {} - # Literal pool: - self.constants = [] - - def argLoc(self, pos): - """ - Gets the function parameter location in IR-code format. - """ - if pos == 0: - return self.p1 - elif pos == 1: - return self.p2 - elif pos == 2: - return self.p3 - elif pos == 3: - return self.p4 - else: - raise NotImplementedError('No more than 4 parameters implemented') - - def allocVar(self, lvar): - if lvar not in self.locVars: - self.locVars[lvar] = self.stacksize - self.stacksize = self.stacksize + 4 - return self.locVars[lvar] - - def addConstant(self, value): - lab_name = '{}_literal_{}'.format(self.name, len(self.constants)) - self.constants.append((lab_name, value)) - return lab_name - - def prologue(self): - """ Returns prologue instruction sequence """ - pre = [ - Label(self.name), # Label indication function - Push({LR, R11}) - ] - if self.stacksize > 0: - pre.append(Sub(SP, SP, self.stacksize)) # Reserve stack space - pre += [ - Mov(R11, SP) # Setup frame pointer - ] - return pre - - def epilogue(self): - """ Return epilogue sequence for a frame. Adjust frame pointer and add constant pool """ - post = [] - if self.stacksize > 0: - post.append(Add(SP, SP, self.stacksize)) - post += [ - Pop({PC, R11}), - Alignment(4) # Align at 4 bytes - ] - # Add constant literals: - for ln, v in self.constants: - post.extend([Label(ln), Dcd(v)]) - return post - - def EntryExitGlue3(self): - """ - Add code for the prologue and the epilogue. Add a label, the - return instruction and the stack pointer adjustment for the frame. - """ - for index, ins in enumerate(self.prologue()): - self.instructions.insert(index, AbstractInstruction(ins)) - - # Postfix code: - for ins in self.epilogue(): - self.instructions.append(AbstractInstruction(ins))
--- a/python/ppci/target/arm/instructions.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,323 +0,0 @@ - -from ..basetarget import Instruction -from ...bitfun import rotate_left - -from .token import ArmToken -from .registers import R0, SP, ArmRegister - - -def encode_imm32(v): - """ Bundle 32 bit value into 4 bits rotation and 8 bits value - """ - for i in range(0, 16): - v2 = rotate_left(v, i*2) - if (v2 & 0xFFFFFF00) == 0: - rotation = i - val = v2 & 0xFF - x = (rotation << 8) | val - return x - raise Exception("Invalid value {}".format(v)) - -# Instructions: - -class ArmInstruction(Instruction): - def __init__(self): - self.token = ArmToken() - - -class Dcd(ArmInstruction): - def __init__(self, v): - super().__init__() - self.v = v - - def encode(self): - self.token[0:32] = self.v - return self.token.encode() - - -def Mov(*args): - if len(args) == 2: - if isinstance(args[1], int): - return Mov1(*args) - elif isinstance(args[1], ArmRegister): - return Mov2(*args) - raise Exception() - - -class Mov1(ArmInstruction): - """ Mov Rd, imm16 """ - def __init__(self, reg, imm): - super().__init__() - assert type(imm) is int - self.reg = reg - self.imm = imm - - def encode(self): - self.token[0:12] = encode_imm32(self.imm) - self.token.Rd = self.reg.num - self.token[16:20] = 0 - self.token[20] = 0 # Set flags - self.token[21:28] = 0b0011101 - self.token.cond = AL - return self.token.encode() - - def relocations(self): - return [] - - def __repr__(self): - return 'Mov {}, {}'.format(self.reg, self.imm) - - -class Mov2(ArmInstruction): - def __init__(self, rd, rm): - super().__init__() - self.rd = rd - self.rm = rm - - def encode(self): - self.token[0:4] = self.rm.num - self.token[4:12] = 0 - self.token[12:16] = self.rd.num - self.token[16:20] = 0 - self.token.S = 0 - self.token[21:28] = 0xD - self.token.cond = AL - return self.token.encode() - - -def Add(*args): - if len(args) == 3 and isinstance(args[0], ArmRegister) and \ - isinstance(args[1], ArmRegister): - if isinstance(args[2], ArmRegister): - return Add1(args[0], args[1], args[2]) - elif isinstance(args[2], int): - return Add2(args[0], args[1], args[2]) - raise Exception() - -def Sub(*args): - if len(args) == 3 and isinstance(args[0], ArmRegister) and \ - isinstance(args[1], ArmRegister): - if isinstance(args[2], ArmRegister): - return Sub1(args[0], args[1], args[2]) - elif isinstance(args[2], int): - return Sub2(args[0], args[1], args[2]) - raise Exception() - -def Mul(*args): - return Mul1(args[0], args[1], args[2]) - - -class Mul(ArmInstruction): - def __init__(self, rd, rn, rm): - super().__init__() - self.rd = rd - self.rn = rn - self.rm = rm - - def encode(self): - self.token[0:4] = self.rn.num - self.token[4:8] = 0b1001 - self.token[8:12] = self.rm.num - self.token[16:20] = self.rd.num - self.token.S = 0 - self.token.cond = AL - return self.token.encode() - - -class OpRegRegReg(ArmInstruction): - """ add rd, rn, rm """ - def __init__(self, rd, rn, rm, shift=0): - super().__init__() - self.rd = rd - self.rn = rn - self.rm = rm - - def encode(self): - self.token[0:4] = self.rm.num - self.token[4] = 0 - self.token[5:7] = 0 - self.token[7:12] = 0 # Shift - self.token.Rd = self.rd.num - self.token.Rn = self.rn.num - self.token.S = 0 # Set flags - self.token[21:28] = self.opcode - self.token.cond = 0xE # Always! - return self.token.encode() - - def __repr__(self): - return 'add {}, {}, {}'.format(self.rd, self.rn, self.rm) - - -class Add1(OpRegRegReg): - opcode = 0b0000100 - - -class Sub1(OpRegRegReg): - opcode = 0b0000010 - - -class Orr1(OpRegRegReg): - opcode = 0b0001100 - - -class OpRegRegImm(ArmInstruction): - """ add rd, rn, imm12 """ - def __init__(self, rd, rn, imm): - super().__init__() - self.rd = rd - self.rn = rn - self.imm2 = encode_imm32(imm) - self.imm = imm - - def encode(self): - self.token[0:12] = self.imm2 - self.token.Rd = self.rd.num - self.token.Rn = self.rn.num - self.token.S = 0 # Set flags - self.token[21:28] = self.opcode - self.token.cond = 0xE # Always! - return self.token.encode() - - def __repr__(self): - return 'add {}, {}, {}'.format(self.rd, self.rn, self.imm) - - -class Add2(OpRegRegImm): - opcode = 0b0010100 - - -class Sub2(OpRegRegImm): - opcode = 0b0010010 - - - -# Branches: - -class BranchBaseRoot(ArmInstruction): - def __init__(self, target): - super().__init__() - self.target = target - - def encode(self): - self.token.cond = self.cond - self.token[24:28] = self.opcode - return self.token.encode() - - def relocations(self): - return [(self.target, 'b_imm24')] - - -EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL = range(15) - -class BranchBase(BranchBaseRoot): - opcode = 0b1010 - -class BranchLinkBase(BranchBaseRoot): - opcode = 0b1011 - -class Bl(BranchLinkBase): - cond = AL - -class B(BranchBase): - cond = AL - -class Beq(BranchBase): - cond = EQ - -class Bgt(BranchBase): - cond = GT - -class Ble(BranchBase): - cond = LE - -class Blt(BranchBase): - cond = LT - - -# Memory: - -def reg_list_to_mask(reg_list): - mask = 0 - for reg in reg_list: - mask |= (1 << reg.num) - return mask - - -class Push(ArmInstruction): - def __init__(self, register_set): - super().__init__() - self.reg_list = register_set - - def encode(self): - self.token.cond = AL - self.token[16:28] = 0b100100101101 - reg_list = 0 - self.token[0:16] = reg_list_to_mask(self.reg_list) - return self.token.encode() - -class Pop(ArmInstruction): - def __init__(self, register_set): - super().__init__() - self.reg_list = register_set - - def encode(self): - self.token.cond = AL - self.token[16:28] = 0b100010111101 - self.token[0:16] = reg_list_to_mask(self.reg_list) - return self.token.encode() - - -def Ldr(*args): - if len(args) == 3 and isinstance(args[1], ArmRegister): - return Ldr1(*args) - elif len(args) == 2 and isinstance(args[1], ArmRegister): - return Ldr1(args[0], args[1], 0) - raise Exception() - -def Str(*args): - if len(args) == 3 and isinstance(args[1], ArmRegister): - return Str1(*args) - elif len(args) == 2 and isinstance(args[1], ArmRegister): - return Str1(args[0], args[1], 0) - raise Exception() - - -class LdrStrBase(ArmInstruction): - def __init__(self, rt, rn, offset): - super().__init__() - self.rt = rt - self.rn = rn - self.offset = offset - - def encode(self): - self.token.cond = AL - self.token.Rn = self.rn.num - self.token[25:28] = self.opcode - self.token[20] = self.bit20 - self.token[12:16] = self.rt.num - self.token[24] = 1 # Index - if self.offset >= 0: - self.token[23] = 1 # U == 1 'add' - self.token[0:12] = self.offset - else: - self.token[23] = 0 - self.token[0:12] = -self.offset - return self.token.encode() - - -class Str1(LdrStrBase): - opcode = 0b010 - bit20 = 0 - - -class Ldr1(LdrStrBase): - opcode = 0b010 - bit20 = 1 - - -class Ldr3(ArmInstruction): - """ Load PC relative constant value """ - def __init__(self, rt, label): - self.rt = rt - self.label = label -
--- a/python/ppci/target/arm/registers.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ - -from ..basetarget import Register - -class ArmRegister(Register): - def __init__(self, num, name): - super().__init__(name) - self.num = num - - def __repr__(self): - return self.name - - -class Reg8Op(ArmRegister): - pass - - -def get_register(n): - for x in registers: - if x.num == n: - return x - raise Exception('No register found with this number') - -def register_range(a, b): - """ Return set of registers from a to b """ - assert a.num < b.num - return {get_register(n) for n in range(a.num, b.num + 1)} - - -R0 = Reg8Op(0, 'r0') -R1 = Reg8Op(1, 'r1') -R2 = Reg8Op(2, 'r2') -R3 = Reg8Op(3, 'r3') -R4 = Reg8Op(4, 'r4') -R5 = Reg8Op(5, 'r5') -R6 = Reg8Op(6, 'r6') -R7 = Reg8Op(7, 'r7') -R8 = ArmRegister(8, 'r8') -R9 = ArmRegister(9, 'r9') -R10 = ArmRegister(10, 'r10') -R11 = ArmRegister(11, 'r11') -R12 = ArmRegister(12, 'r12') - -# Other registers: -# TODO -SP = ArmRegister(13, 'sp') -LR = ArmRegister(14, 'lr') -PC = ArmRegister(15, 'pc') - -registers = [R0, R1, R2, R3, R4, R5, R6, R7, SP, LR, PC]
--- a/python/ppci/target/arm/selector.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -from ... import ir, same_dir -from ppci.irmach import AbstractInstruction as makeIns -from ppci.ir2tree import makeTree -from .instructions import Str1, Mov2 -from .instructions import B, Bl, Blt, Bgt, Beq -import pyburg -from ..basetarget import Nop -from ..instructionselector import InstructionSelector - -# Import BURG spec for arm: -spec_file = same_dir(__file__, 'arm.brg') -arm_matcher = pyburg.load_as_module(spec_file) - - -class ArmMatcher(arm_matcher.Matcher): - """ Matcher that derives from a burg spec generated matcher """ - def __init__(self, selector): - super().__init__() - self.newTmp = selector.newTmp - self.emit = selector.emit - self.selector = selector - - -class ArmInstructionSelector(InstructionSelector): - """ Instruction selector for the arm architecture """ - def __init__(self): - super().__init__() - self.matcher = ArmMatcher(self) - - def munchExpr(self, e): - # Use BURG system here: - t = makeTree(e) - return self.matcher.gen(t) - - def munchCall(self, e): - """ Generate code for call sequence """ - # Move arguments into proper locations: - reguses = [] - for i, a in enumerate(e.arguments): - loc = self.frame.argLoc(i) - m = ir.Move(loc, a) - self.munchStm(m) - if isinstance(loc, ir.Temp): - reguses.append(loc) - self.emit(Bl(e.f), src=reguses, dst=[self.frame.rv]) - d = self.newTmp() - self.move(d, self.frame.rv) - return d - - def munchStm(self, s): - if isinstance(s, ir.Terminator): - pass - elif isinstance(s, ir.Move) and isinstance(s.dst, ir.Mem) and \ - isinstance(s.dst.e, ir.Binop) and s.dst.e.operation == '+' and \ - isinstance(s.dst.e.b, ir.Const): - a = self.munchExpr(s.dst.e.a) - val = self.munchExpr(s.src) - c = s.dst.e.b.value - self.emit(Str1, others=[c], src=[a, val]) - elif isinstance(s, ir.Move) and isinstance(s.dst, ir.Mem): - memloc = self.munchExpr(s.dst.e) - val = self.munchExpr(s.src) - self.emit(Str1, others=[0], src=[memloc, val]) - elif isinstance(s, ir.Move) and isinstance(s.dst, ir.Temp): - val = self.munchExpr(s.src) - dreg = s.dst - self.move(dreg, val) - elif isinstance(s, ir.Exp): - # Generate expression code and discard the result. - x = self.munchExpr(s.e) - self.emit(Nop(), src=[x]) - elif isinstance(s, ir.Jump): - tgt = self.targets[s.target] - self.emit(B(ir.label_name(s.target)), jumps=[tgt]) - elif isinstance(s, ir.CJump): - a = self.munchExpr(s.a) - b = self.munchExpr(s.b) - self.emit(Cmp, src=[a, b]) - ntgt = self.targets[s.lab_no] - ytgt = self.targets[s.lab_yes] - jmp_ins = makeIns(B(ir.label_name(s.lab_no)), jumps=[ntgt]) - opnames = {'<': Blt, '>':Bgt, '==':Beq, '!=':Bne} - op = opnames[s.cond](ir.label_name(s.lab_yes)) - self.emit(op, jumps=[ytgt, jmp_ins]) # Explicitely add fallthrough - self.emit2(jmp_ins) - else: - raise NotImplementedError('Stmt --> {}'.format(s)) - - def move(self, dst, src): - self.emit(Mov2, src=[src], dst=[dst], ismove=True)
--- a/python/ppci/target/arm/token.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ - -from ..token import Token, u32, bit_range - - -class ArmToken(Token): - def __init__(self): - super().__init__(32) - - cond = bit_range(28, 32) - S = bit_range(20, 21) - Rd = bit_range(12, 16) - Rn = bit_range(16, 20) - - def encode(self): - return u32(self.bit_value)
--- a/python/ppci/target/basetarget.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -from ppci import CompilerError - -""" - Base classes for defining a target -""" - -class Instruction: - """ Base instruction class """ - def encode(self): - return bytes() - - def relocations(self): - return [] - - def symbols(self): - return [] - - -class Nop(Instruction): - """ Instruction that does nothing and has zero size """ - def encode(self): - return bytes() - - -class PseudoInstruction(Instruction): - pass - - -class Label(PseudoInstruction): - def __init__(self, name): - self.name = name - - def __repr__(self): - return '{}:'.format(self.name) - - def symbols(self): - return [self.name] - - -class Comment(PseudoInstruction): - def __init__(self, txt): - self.txt = txt - - def encode(self): - return bytes() - - def __repr__(self): - return '; {}'.format(self.txt) - - -class Alignment(PseudoInstruction): - def __init__(self, a): - self.align = a - - def __repr__(self): - return 'ALIGN({})'.format(self.align) - - def encode(self): - pad = [] - # TODO - address = 0 - while (address % self.align) != 0: - address += 1 - pad.append(0) - return bytes(pad) - - -class DebugInfo(PseudoInstruction): - def __init__(self, i): - self.info = i - - def __repr__(self): - return 'DebugInfo: {}'.format(self.info) - - -class Register: - def __init__(self, name): - self.name = name - - -class Target: - def __init__(self, name, desc=''): - self.name = name - self.desc = desc - self.registers = [] - self.byte_sizes = {'int' : 4} # For front end! - - # For lowering: - self.lower_functions = {} - - # For assembler: - self.assembler_rules = [] - self.asm_keywords = [] - - self.generate_base_rules() - - def generate_base_rules(self): - # Base rules for constants: - self.add_rule('imm32', ['val32'], lambda x: x[0].val) - self.add_rule('imm32', ['imm16'], lambda x: x[0]) - - self.add_rule('imm16', ['val16'], lambda x: x[0].val) - self.add_rule('imm16', ['imm12'], lambda x: x[0]) - - self.add_rule('imm12', ['val12'], lambda x: x[0].val) - self.add_rule('imm12', ['imm8'], lambda x: x[0]) - - self.add_rule('imm8', ['val8'], lambda x: x[0].val) - self.add_rule('imm8', ['imm5'], lambda x: x[0]) - - self.add_rule('imm5', ['val5'], lambda x: x[0].val) - self.add_rule('imm5', ['imm3'], lambda x: x[0]) - - self.add_rule('imm3', ['val3'], lambda x: x[0].val) - - def add_keyword(self, kw): - self.asm_keywords.append(kw) - - def add_instruction(self, rhs, f): - self.add_rule('instruction', rhs, f) - - def add_rule(self, lhs, rhs, f): - self.assembler_rules.append((lhs, rhs, f)) - - def lower_frame_to_stream(self, frame, outs): - """ Lower instructions from frame to output stream """ - for im in frame.instructions: - if isinstance(im.assem, Instruction): - outs.emit(im.assem) - else: - ins = self.lower_functions[im.assem](im) - outs.emit(ins) - - def add_lowering(self, cls, f): - """ Add a function to the table of lowering options for this target """ - self.lower_functions[cls] = f
--- a/python/ppci/target/instructionselector.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -from ppci import ir -from ppci import irmach -from ppci.irmach import AbstractInstruction as makeIns -from .basetarget import Label - -def genTemps(): - n = 900 - while True: - yield ir.Temp('t{}'.format(n)) - n = n + 1 - - -class InstructionSelector: - """ - Base instruction selector. This class must be overridden by - backends. - """ - def __init__(self): - self.temps = genTemps() - - def newTmp(self): - return self.temps.__next__() - - def munchFunction(self, f, frame): - # Entry point for instruction selection - assert isinstance(f, ir.Function) - self.targets = {} - # Enter a frame per function: - self.frame = frame - # First define labels: - for bb in f.Blocks: - itgt = makeIns(Label(ir.label_name(bb))) - self.targets[bb] = itgt - # Generate code for all blocks: - for bb in f.Blocks: - self.emit2(self.targets[bb]) - for i in bb.Instructions: - self.munchStm(i) - self.munchStm(ir.Move(self.frame.rv, f.return_value)) - - def move(self, dst, src): - raise NotImplementedError('Not target implemented') - - def emit(self, *args, **kwargs): - """ Abstract instruction emitter """ - i = makeIns(*args, **kwargs) - return self.emit2(i) - - def emit2(self, i): - self.frame.instructions.append(i) - return i - - def munchStm(self, s): - """ Implement this in the target specific back-end """ - raise NotImplementedError() - - def munchExpr(self, e): - """ Implement this in the target specific back-end """ - raise NotImplementedError()
--- a/python/ppci/target/msp430/instructions.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,211 +0,0 @@ - -from ..basetarget import Register, Instruction, Target -from ..token import Token, u16, bit_range -from .registers import Msp430Register - - -class Msp430Token(Token): - def __init__(self): - super().__init__(16) - - condition = bit_range(10, 13) - opcode = bit_range(12, 16) - register = bit_range(0, 4) - destination = bit_range(0, 4) - source = bit_range(8, 12) - bw = bit_range(6, 7) # TODO: actually a single bit! - Ad = bit_range(7, 8) # TODO: actually a single bit! - As = bit_range(4, 6) - - def encode(self): - return u16(self.bit_value) - -REGISTER_MODE = 1 -SYMBOLIC_MODE = 3 -ABSOLUTE_MODE = 4 -#TODO: add more modes! -IMMEDIATE_MODE = 7 - -class Msp430Operand: - pass - -class Msp430DestinationOperand(Msp430Operand): - def __init__(self, param): - if isinstance(param, Msp430Register): - self.reg = param.num - self.Ad = 0 - else: - raise Exception() - - -class Msp430SourceOperand(Msp430Operand): - def __init__(self, param): - if isinstance(param, Msp430Register): - self.reg = param.num - self.As = 0 - self.extra_bytes = bytes() - elif isinstance(param, int): - self.reg = 0 - self.As = 3 - self.extra_bytes = u16(param) - else: - raise Exception() - - -class Msp430Instruction(Instruction): - b = 0 - def __init__(self): - self.token = Msp430Token() - - -class Reti(Msp430Instruction): - def encode(self): - self.token[0:16] = 0x1300 - return self.token.encode() - - -######################### -# Jump instructions: -######################### - -class JumpInstruction(Msp430Instruction): - def __init__(self, target): - super().__init__() - self.target = target - - def encode(self): - self.token.condition = self.condition - self.token.offset = 0 - self.token[13] = 1 - return self.token.encode() - - def relocations(self): - return [(self.target, 'msp_reloc')] - - -class Jnz(JumpInstruction): - condition = 0 - - -class Jz(JumpInstruction): - condition = 1 - - -class Jnc(JumpInstruction): - condition = 2 - - -class Jc(JumpInstruction): - condition = 3 - - -class Jn(JumpInstruction): - condition = 4 - - -class Jge(JumpInstruction): - condition = 5 - - -class Jl(JumpInstruction): - condition = 6 - - -class Jmp(JumpInstruction): - condition = 7 - - -######################### -# Single operand arithmatic: -######################### - - -class OneOpArith(Msp430Instruction): - def __init__(self, op1): - self.op1 = op1 - - def encode(self): - # TODO: - bits[15:10] = '00100' - h1 = (self.opcode << 4) - return pack_ins(h1) - - -def oneOpIns(mne, opc): - """ Helper function to define a one operand arithmetic instruction """ - members = {'opcode': opc} - ins_cls = type(mne + '_ins', (OneOpArith,), members) - - -oneOpIns('rrc', 0) -oneOpIns('swpb', 1) -oneOpIns('rra', 2) -oneOpIns('sxt', 3) -oneOpIns('push', 4) -oneOpIns('call', 5) - - -######################### -# Two operand arithmatic instructions: -######################### - - -class TwoOpArith(Msp430Instruction): - def __init__(self, src, dst): - super().__init__() - self.src = Msp430SourceOperand(src) - self.dst = Msp430DestinationOperand(dst) - - def encode(self): - """ - Smart things have been done by MSP430 designers. - As (2 bits) is the source addressing mode selector. - Ad (1 bit) is the destination adressing mode selector. - For the source there are 7 different addressing mode. - For the destination there are 4. - The trick is to use also the register to distuingish the - different modes. - """ - # TODO: Make memory also possible - self.token.bw = self.b # When b=1, the operation is byte mode - self.token.As = self.src.As - self.token.Ad = self.dst.Ad - self.token.destination = self.dst.reg - self.token.source = self.src.reg - self.token.opcode = self.opcode - return self.token.encode() + self.src.extra_bytes - - -def twoOpIns(mne, opc): - """ Helper function to define a two operand arithmetic instruction """ - members = {'opcode': opc} - ins_cls = type(mne + '_ins', (TwoOpArith,), members) - - -class Mov(TwoOpArith): - """ Moves the source to the destination """ - opcode = 4 - - -# This is equivalent to the helper function twoOpIns: -class Add(TwoOpArith): - """ Adds the source to the destination """ - mnemonic = 'add' - opcode = 5 - - -twoOpIns('addc', 6) -twoOpIns('subc', 7) -twoOpIns('sub', 8) - - -class Cmp(TwoOpArith): - opcode = 9 - - -twoOpIns('dadd', 10) -twoOpIns('bit', 11) -twoOpIns('bic', 12) -twoOpIns('bis', 13) -twoOpIns('xor', 14) -twoOpIns('and', 15)
--- a/python/ppci/target/msp430/msp430.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -import struct -import types -from ..basetarget import Register, Instruction, Target -from ppci import CompilerError -from .registers import r10, r11, r12, r13, r14, r15 -from .instructions import Reti, Mov, Add - -# Create the target class (singleton): - -class Msp430Target(Target): - def __init__(self): - super().__init__('msp430') - - # Registers: - self.add_keyword('r10') - self.add_keyword('r11') - self.add_keyword('r12') - self.add_keyword('r13') - self.add_keyword('r14') - self.add_keyword('r15') - self.add_rule('reg', ['r10'], lambda rhs: r10) - self.add_rule('reg', ['r11'], lambda rhs: r11) - self.add_rule('reg', ['r12'], lambda rhs: r12) - self.add_rule('reg', ['r13'], lambda rhs: r13) - self.add_rule('reg', ['r14'], lambda rhs: r14) - self.add_rule('reg', ['r15'], lambda rhs: r15) - - # Instructions rules: - self.add_keyword('mov') - self.add_instruction(['mov', 'reg', ',', 'reg'], - lambda rhs: Mov(rhs[1], rhs[3])) - self.add_instruction(['mov', 'imm16', ',', 'reg'], - lambda rhs: Mov(rhs[1], rhs[3])) - - self.add_keyword('add') - self.add_instruction(['add', 'reg', ',', 'reg'], - lambda rhs: Add(rhs[1], rhs[3])) - - self.add_keyword('reti') - self.add_instruction(['reti'], lambda rhs: Reti()) - - - -msp430target = Msp430Target() - - -# Target description for the MSP430 processor - - -msp430target.registers.append(r10) -msp430target.registers.append(r11) -msp430target.registers.append(r12) -msp430target.registers.append(r13) -msp430target.registers.append(r14) -msp430target.registers.append(r15) -
--- a/python/ppci/target/msp430/registers.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ - -from ..basetarget import Register - -class Msp430Register(Register): - def __init__(self, num, name): - super().__init__(name) - self.num = num - -# 8 bit registers: -PCB = Msp430Register(0, 'r0') -rpc = PCB -r10 = Msp430Register(10, 'r10') -r11 = Msp430Register(11, 'r11') -r12 = Msp430Register(12, 'r12') -r13 = Msp430Register(13, 'r13') -r14 = Msp430Register(14, 'r14') -r15 = Msp430Register(15, 'r15') -
--- a/python/ppci/target/target_list.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ - -from .arm import ArmTarget -from .thumb import ThumbTarget -from .msp430.msp430 import msp430target - -# Instance: -arm_target = ArmTarget() -thumb_target = ThumbTarget() - -target_list = [arm_target, thumb_target]
--- a/python/ppci/target/thumb/__init__.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ - - -from .armtarget import ThumbTarget -
--- a/python/ppci/target/thumb/arm.brg Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ - -from ppci.target.thumb.instructions import Orr, Lsl, Str2, Ldr2, Ldr3 -from ppci.target.thumb.instructions import B, Bl, Bgt, Blt, Beq, Bne -from ppci.target.thumb.instructions import Mov2, Mov3, Sub3 -from ppci.target.thumb.instructions import Add3, Sub, Cmp, Sub2, Add2, Mul - -%% - -%terminal ADDI32 SUBI32 MULI32 -%terminal ORI32 SHLI32 -%terminal CONSTI32 MEMI32 REGI32 CALL -%terminal MOVI32 - -%% - - -reg: ADDI32(reg, reg) 2 (. d = self.newTmp(); self.emit(Add3, dst=[d], src=[$1, $2]); return d .) -reg: SUBI32(reg, reg) 2 (. d = self.newTmp(); self.emit(Sub3, dst=[d], src=[$1, $2]); return d .) -reg: ORI32(reg, reg) 2 (. d = self.newTmp(); self.selector.move(d, $1); self.emit(Orr, dst=[], src=[d, $2]); return d .) -reg: SHLI32(reg, reg) 2 (. d = self.newTmp(); self.selector.move(d, $1); self.emit(Lsl, dst=[], src=[d, $2]); return d .) -reg: MULI32(reg, reg) 2 (. d = self.newTmp(); self.selector.move(d, $1); self.emit(Mul, dst=[d], src=[$2, d]); return d .) - -reg: CONSTI32 3 (. d = self.newTmp(); ln = self.selector.frame.addConstant($$.value); self.emit(Ldr3, dst=[d], others=[ln]); return d .) -reg: MEMI32(reg) 4 (. d = self.newTmp(); self.emit(Ldr2, dst=[d], src=[$1], others=[0]); return d .) -reg: REGI32 1 (. return $$.value .) -reg: CALL 1 (. return self.selector.munchCall($$.value) .) - - -stmt: MOVI32(MEMI32(addr), reg) 3 (. self.emit(Str2, src=[$1, $2]) .) - -addr: reg 2 (. .)
--- a/python/ppci/target/thumb/arminstructionselector.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -from ... import ir, same_dir -from ppci.irmach import AbstractInstruction as makeIns -from ppci.ir2tree import makeTree -import pyburg -from ..basetarget import Nop -from ..instructionselector import InstructionSelector -from .instructions import Orr, Lsl, Str2, Ldr2, Ldr3 -from .instructions import B, Bl, Bgt, Blt, Beq, Bne -from .instructions import Mov2, Mov3 -from .instructions import Cmp, Sub2, Mul - -# Import BURG spec for arm: -spec_file = same_dir(__file__, 'arm.brg') -arm_matcher = pyburg.load_as_module(spec_file) - - -class ArmMatcher(arm_matcher.Matcher): - """ Matcher that derives from a burg spec generated matcher """ - def __init__(self, selector): - super().__init__() - self.newTmp = selector.newTmp - self.emit = selector.emit - self.selector = selector - - -class ArmInstructionSelector(InstructionSelector): - """ Instruction selector for the arm architecture """ - def __init__(self): - super().__init__() - self.matcher = ArmMatcher(self) - - def munchExpr(self, e): - # Use BURG system here: - t = makeTree(e) - return self.matcher.gen(t) - - def munchCall(self, e): - """ Generate code for call sequence """ - # Move arguments into proper locations: - reguses = [] - for i, a in enumerate(e.arguments): - loc = self.frame.argLoc(i) - m = ir.Move(loc, a) - self.munchStm(m) - if isinstance(loc, ir.Temp): - reguses.append(loc) - self.emit(Bl(e.f), src=reguses, dst=[self.frame.rv]) - d = self.newTmp() - self.move(d, self.frame.rv) - return d - - def munchStm(self, s): - if isinstance(s, ir.Terminator): - pass - elif isinstance(s, ir.Move) and isinstance(s.dst, ir.Mem) and \ - isinstance(s.dst.e, ir.Binop) and s.dst.e.operation == '+' and \ - isinstance(s.dst.e.b, ir.Const): - a = self.munchExpr(s.dst.e.a) - val = self.munchExpr(s.src) - c = s.dst.e.b.value - self.emit(Str2, others=[c], src=[a, val]) - elif isinstance(s, ir.Move) and isinstance(s.dst, ir.Mem): - memloc = self.munchExpr(s.dst.e) - val = self.munchExpr(s.src) - self.emit(Str2, others=[0], src=[memloc, val]) - elif isinstance(s, ir.Move) and isinstance(s.dst, ir.Temp): - val = self.munchExpr(s.src) - dreg = s.dst - self.move(dreg, val) - elif isinstance(s, ir.Exp): - # Generate expression code and discard the result. - x = self.munchExpr(s.e) - self.emit(Nop(), src=[x]) - elif isinstance(s, ir.Jump): - tgt = self.targets[s.target] - self.emit(B(ir.label_name(s.target)), jumps=[tgt]) - elif isinstance(s, ir.CJump): - a = self.munchExpr(s.a) - b = self.munchExpr(s.b) - self.emit(Cmp, src=[a, b]) - ntgt = self.targets[s.lab_no] - ytgt = self.targets[s.lab_yes] - jmp_ins = makeIns(B(ir.label_name(s.lab_no)), jumps=[ntgt]) - opnames = {'<': Blt, '>':Bgt, '==':Beq, '!=':Bne} - op = opnames[s.cond](ir.label_name(s.lab_yes)) - self.emit(op, jumps=[ytgt, jmp_ins]) # Explicitely add fallthrough - self.emit2(jmp_ins) - else: - raise NotImplementedError('Stmt --> {}'.format(s)) - - def move(self, dst, src): - self.emit(Mov2, src=[src], dst=[dst], ismove=True)
--- a/python/ppci/target/thumb/armtarget.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,164 +0,0 @@ -import struct -from ..basetarget import Register, Instruction, Target, Label, Alignment -from .instructions import Add2, Sub, Sub3, Add3, Cmp, Lsl, Orr, Add, Cmp2, Sub2, Add2, Mul, And -from .instructions import Dcd, Pop, Push, Yield, Mov2, Mov3 -from .instructions import B, Bl, Bne, Beq, Blt, Bgt -from .instructions import Ldr, Str2, Ldr2, Str1, Ldr1, Ldr3 - -from .frame import ArmFrame -from .arminstructionselector import ArmInstructionSelector -from ..arm.registers import R0, R1, R2, R3, R4, R5, R6, R7, SP, LR, PC -from ..arm.registers import register_range - - -""" ARM target description. """ - -# TODO: encode this in DSL (domain specific language) -# TBD: is this required? -# TODO: make a difference between armv7 and armv5? - -thumb_assembly_rules = [] -def add_rule(rhs, f): - thumb_assembly_rules.append(('instruction', rhs, f)) - - -class ThumbTarget(Target): - def __init__(self): - super().__init__('thumb') - self.ins_sel = ArmInstructionSelector() - self.FrameClass = ArmFrame - self.add_rules() - - # Add lowering options: - self.add_lowering(Str2, lambda im: Str2(im.src[1], im.src[0], im.others[0])) - self.add_lowering(Ldr2, lambda im: Ldr2(im.dst[0], im.src[0], im.others[0])) - self.add_lowering(Ldr3, lambda im: Ldr3(im.dst[0], im.others[0])) - self.add_lowering(Mov3, lambda im: Mov3(im.dst[0], im.others[0])) - self.add_lowering(Add2, lambda im: Add2(im.dst[0], im.src[0], im.others[0])) - self.add_lowering(Sub2, lambda im: Sub2(im.dst[0], im.src[0], im.others[0])) - self.add_lowering(Mov2, lambda im: Mov2(im.dst[0], im.src[0])) - self.add_lowering(Add3, lambda im: Add3(im.dst[0], im.src[0], im.src[1])) - self.add_lowering(Sub3, lambda im: Sub3(im.dst[0], im.src[0], im.src[1])) - self.add_lowering(Mul, lambda im: Mul(im.src[0], im.dst[0])) - self.add_lowering(And, lambda im: And(im.src[0], im.src[1])) - self.add_lowering(Orr, lambda im: Orr(im.src[0], im.src[1])) - self.add_lowering(Lsl, lambda im: Lsl(im.src[0], im.src[1])) - self.add_lowering(Cmp, lambda im: Cmp(im.src[0], im.src[1])) - - def add_rules(self): - - # Add instructions: - self.add_keyword('dcd') - self.add_instruction(['dcd', 'imm32'], lambda rhs: Dcd(rhs[1])) - - self.add_keyword('mov') - self.add_instruction(['mov', 'reg8', ',', 'reg8'], - lambda rhs: Mov2(rhs[1], rhs[3])) - - self.add_instruction(['mov', 'reg8', ',', 'imm8'], - lambda rhs: Mov3(rhs[1], rhs[3])) - - self.add_keyword('add') - self.add_instruction(['add', 'reg8', ',', 'reg8', ',', 'imm3'], - lambda rhs: Add2(rhs[1], rhs[3], rhs[5])) - - self.add_instruction(['add', 'reg8', ',', 'reg8', ',', 'reg8'], - lambda rhs: Add3(rhs[1], rhs[3], rhs[5])) - - self.add_keyword('sub') - self.add_instruction(['sub', 'reg8', ',', 'reg8', ',', 'imm3'], - lambda rhs: Sub(rhs[1], rhs[3], rhs[5])) - - self.add_instruction(['sub', 'sp', ',', 'sp', ',', 'imm8'], - lambda rhs: Sub(SP, SP, rhs[5])) - - self.add_instruction(['add', 'sp', ',', 'sp', ',', 'imm8'], - lambda rhs: Add(SP, SP, rhs[5])) - - self.add_keyword('cmp') - self.add_instruction(['cmp', 'reg8', ',', 'reg8'], - lambda rhs: Cmp(rhs[1], rhs[3])) - self.add_instruction(['cmp', 'reg8', ',', 'imm8'], - lambda rhs: Cmp2(rhs[1], rhs[3])) - - self.add_keyword('lsl') - self.add_instruction(['lsl', 'reg8', ',', 'reg8'], - lambda rhs: Lsl(rhs[1], rhs[3])) - - self.add_keyword('str') - self.add_instruction(['str', 'reg8', ',', '[', 'reg8', '+', 'imm5', ']'], - lambda rhs: Str2(rhs[1], rhs[4], rhs[6])) - - self.add_keyword('ldr') - self.add_instruction(['ldr', 'reg8', ',', '[', 'reg8', '+', 'imm5', ']'], - lambda rhs: Ldr2(rhs[1], rhs[4], rhs[6])) - - self.add_instruction(['str', 'reg8', ',', '[', 'sp', '+', 'imm8', ']'], - lambda rhs: Str1(rhs[1], rhs[6])) - - self.add_instruction(['ldr', 'reg8', ',', '[', 'sp', '+', 'imm8', ']'], - lambda rhs: Ldr1(rhs[1], rhs[6])) - - self.add_keyword('pop') - self.add_instruction(['pop', 'reg_list'], lambda rhs: Pop(rhs[1])) - self.add_keyword('push') - self.add_instruction(['push', 'reg_list'], lambda rhs: Push(rhs[1])) - - self.add_keyword('yield') - self.add_instruction(['yield'], lambda rhs: Yield()) - - self.add_keyword('b') - self.add_keyword('bl') - self.add_instruction(['b', 'ID'], lambda rhs: B(rhs[1].val)) - self.add_instruction(['bl', 'ID'], lambda rhs: Bl(rhs[1].val)) - self.add_keyword('beq') - self.add_keyword('bne') - self.add_keyword('blt') - self.add_keyword('bgt') - self.add_instruction(['beq', 'ID'], lambda rhs: Beq(rhs[1].val)) - self.add_instruction(['bne', 'ID'], lambda rhs: Bne(rhs[1].val)) - self.add_instruction(['blt', 'ID'], lambda rhs: Blt(rhs[1].val)) - self.add_instruction(['bgt', 'ID'], lambda rhs: Bgt(rhs[1].val)) - - self.add_keyword('align') - self.add_instruction(['align', 'imm8'], lambda rhs: Alignment(rhs[1])) - - self.add_instruction(['ldr', 'reg8', ',', 'ID'], - lambda rhs: Ldr(rhs[1], rhs[3].val)) - - # Additional rules: - - # Register list grammar: - self.add_rule('reg_list', ['{', 'reg_list_inner', '}'], - lambda rhs: rhs[1]) - self.add_rule('reg_list_inner', ['reg_or_range'], - lambda rhs: rhs[0]) - self.add_rule('reg_list_inner', ['reg_or_range', ',', 'reg_list_inner'], - lambda rhs: rhs[0] | rhs[2]) - self.add_rule('reg_or_range', ['reg8'], lambda rhs: {rhs[0]}) - self.add_rule('reg_or_range', ['lr'], lambda rhs: {LR}) - self.add_rule('reg_or_range', ['pc'], lambda rhs: {PC}) - - self.add_rule('reg_or_range', ['reg8', '-', 'reg8'], - lambda rhs: register_range(rhs[0], rhs[2])) - - self.add_keyword('r0') - self.add_keyword('r1') - self.add_keyword('r2') - self.add_keyword('r3') - self.add_keyword('r4') - self.add_keyword('r5') - self.add_keyword('r6') - self.add_keyword('r7') - self.add_keyword('sp') - self.add_keyword('lr') - self.add_keyword('pc') - self.add_rule('reg8', ['r0'], lambda rhs: R0) - self.add_rule('reg8', ['r1'], lambda rhs: R1) - self.add_rule('reg8', ['r2'], lambda rhs: R2) - self.add_rule('reg8', ['r3'], lambda rhs: R3) - self.add_rule('reg8', ['r4'], lambda rhs: R4) - self.add_rule('reg8', ['r5'], lambda rhs: R5) - self.add_rule('reg8', ['r6'], lambda rhs: R6) - self.add_rule('reg8', ['r7'], lambda rhs: R7) -
--- a/python/ppci/target/thumb/armtoken.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ - -from ..token import Token, u16, bit_range - -class ThumbToken(Token): - def __init__(self): - super().__init__(16) - - rd = bit_range(0, 3) - - def encode(self): - return u16(self.bit_value) -
--- a/python/ppci/target/thumb/frame.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -from ... import ir -from ..basetarget import Label, Alignment -from ...irmach import AbstractInstruction, Frame -from .instructions import Dcd, AddSp, SubSp, Push, Pop, Mov2 -from ..arm.registers import R0, R1, R2, R3, R4, R5, R6, R7, LR, PC, SP - - -class ArmFrame(Frame): - """ Arm specific frame for functions. """ - def __init__(self, name): - # We use r7 as frame pointer. - super().__init__(name) - self.regs = [R0, R1, R2, R3, R4, R5, R6] - self.rv = ir.Temp('special_RV') - self.p1 = ir.Temp('special_P1') - self.p2 = ir.Temp('special_P2') - self.p3 = ir.Temp('special_P3') - self.p4 = ir.Temp('special_P4') - self.fp = ir.Temp('special_FP') - # Pre-colored registers: - self.tempMap = {} - self.tempMap[self.rv] = R0 - self.tempMap[self.p1] = R1 - self.tempMap[self.p2] = R2 - self.tempMap[self.p3] = R3 - self.tempMap[self.p4] = R4 - self.tempMap[self.fp] = R7 - self.locVars = {} - self.parMap = {} - # Literal pool: - self.constants = [] - - def argLoc(self, pos): - """ - Gets the function parameter location in IR-code format. - """ - if pos == 0: - return self.p1 - elif pos == 1: - return self.p2 - elif pos == 2: - return self.p3 - elif pos == 3: - return self.p4 - else: - raise NotImplementedError('No more than 4 parameters implemented') - - def allocVar(self, lvar): - if lvar not in self.locVars: - self.locVars[lvar] = self.stacksize - self.stacksize = self.stacksize + 4 - return self.locVars[lvar] - - def addConstant(self, value): - lab_name = '{}_literal_{}'.format(self.name, len(self.constants)) - self.constants.append((lab_name, value)) - return lab_name - - def prologue(self): - """ Returns prologue instruction sequence """ - pre = [ - Label(self.name), # Label indication function - Push({LR, R7}) - ] - if self.stacksize > 0: - pre.append(SubSp(self.stacksize)) # Reserve stack space - pre += [ - Mov2(R7, SP) # Setup frame pointer - ] - return pre - - def epilogue(self): - """ Return epilogue sequence for a frame. Adjust frame pointer and add constant pool """ - post = [] - if self.stacksize > 0: - post.append(AddSp(self.stacksize)) - post += [ - Pop({PC, R7}), - Alignment(4) # Align at 4 bytes - ] - # Add constant literals: - for ln, v in self.constants: - post.extend([Label(ln), Dcd(v)]) - return post - - def EntryExitGlue3(self): - """ - Add code for the prologue and the epilogue. Add a label, the - return instruction and the stack pointer adjustment for the frame. - """ - for index, ins in enumerate(self.prologue()): - self.instructions.insert(index, AbstractInstruction(ins)) - - # Postfix code: - for ins in self.epilogue(): - self.instructions.append(AbstractInstruction(ins))
--- a/python/ppci/target/thumb/instructions.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,489 +0,0 @@ -from ..basetarget import Register, Instruction, Target, Label -from ..token import u16, u32 -from .armtoken import ThumbToken -from ..arm.registers import R0, ArmRegister, SP - - -# Instructions: - -class ThumbInstruction(Instruction): - pass - - -class Dcd(ThumbInstruction): - def __init__(self, expr): - if isinstance(expr, int): - self.expr = expr - self.label = None - else: - raise NotImplementedError() - - def encode(self): - return u32(self.expr) - - def relocations(self): - return [] - - def __repr__(self): - return 'DCD 0x{0:X}'.format(self.expr) - - -class nop_ins(ThumbInstruction): - def encode(self): - return bytes() - - def __repr__(self): - return 'NOP' - - -# Memory related - -class LS_imm5_base(ThumbInstruction): - """ ??? Rt, [Rn, imm5] """ - def __init__(self, rt, rn, imm5): - assert imm5 % 4 == 0 - self.imm5 = imm5 >> 2 - self.rn = rn - self.rt = rt - assert self.rn.num < 8 - assert self.rt.num < 8 - self.token = ThumbToken() - - def encode(self): - Rn = self.rn.num - Rt = self.rt.num - imm5 = self.imm5 - self.token[0:3] = Rt - self.token[3:6] = Rn - self.token[6:11] = imm5 - self.token[11:16] = self.opcode - return self.token.encode() - - def __repr__(self): - mnemonic = "???" - return '{} {}, [{}, {}]'.format(mnemonic, self.rt, self.rn, self.imm5) - - -class Str2(LS_imm5_base): - opcode = 0xC - - -class Ldr2(LS_imm5_base): - opcode = 0xD - - -class ls_sp_base_imm8(ThumbInstruction): - def __init__(self, rt, offset): - self.rt = rt - self.offset = offset - - def encode(self): - rt = self.rt.num - assert rt < 8 - imm8 = self.offset >> 2 - assert imm8 < 256 - h = (self.opcode << 8) | (rt << 8) | imm8 - return u16(h) - - def __repr__(self): - mnemonic = self.__class__.__name__ - return '{} {}, [sp,#{}]'.format(mnemonic, self.rt, self.offset) - - -def Ldr(*args): - if len(args) == 2 and isinstance(args[0], ArmRegister) \ - and isinstance(args[1], str): - return Ldr3(*args) - else: - raise Exception() - - -class Ldr3(ThumbInstruction): - """ ldr Rt, LABEL, load value from pc relative position """ - def __init__(self, rt, label): - self.rt = rt - self.label = label - - def relocations(self): - return [(self.label, 'lit_add_8')] - - def encode(self): - rt = self.rt.num - assert rt < 8 - imm8 = 0 - h = (0x9 << 11) | (rt << 8) | imm8 - return u16(h) - - def __repr__(self): - return 'LDR {}, {}'.format(self.rt, self.label) - - -class Ldr1(ls_sp_base_imm8): - """ ldr Rt, [SP, imm8] """ - opcode = 0x98 - - -class Str1(ls_sp_base_imm8): - """ str Rt, [SP, imm8] """ - opcode = 0x90 - - -class Mov3(ThumbInstruction): - """ mov Rd, imm8, move immediate value into register """ - opcode = 4 # 00100 Rd(3) imm8 - def __init__(self, rd, imm): - assert imm < 256 - self.imm = imm - self.rd = rd - self.token = ThumbToken() - - def encode(self): - rd = self.rd.num - self.token[8:11] = rd - self.token[0:8] = self.imm - self.token[11:16] = self.opcode - return self.token.encode() - - def __repr__(self): - return 'MOV {}, {}'.format(self.rd, self.imm) - - -# Arithmatics: - - -class regregimm3_base(ThumbInstruction): - def __init__(self, rd, rn, imm3): - self.rd = rd - self.rn = rn - assert imm3 < 8 - self.imm3 = imm3 - self.token = ThumbToken() - - def encode(self): - rd = self.rd.num - self.token[0:3] = rd - self.token[3:6] = self.rn.num - self.token[6:9] = self.imm3 - self.token[9:16] = self.opcode - return self.token.encode() - - def __repr__(self): - mnemonic = self.__class__.__name__ - return '{} {}, {}, {}'.format(mnemonic, self.rd, self.rn, self.imm3) - - - -class Add2(regregimm3_base): - """ add Rd, Rn, imm3 """ - opcode = 0b0001110 - - -class Sub2(regregimm3_base): - """ sub Rd, Rn, imm3 """ - opcode = 0b0001111 - - -def Sub(*args): - if len(args) == 3 and args[0] is SP and args[1] is SP and \ - isinstance(args[2], int) and args[2] < 256: - return SubSp(args[2]) - elif len(args) == 3 and isinstance(args[0], ArmRegister) and \ - isinstance(args[1], ArmRegister) and isinstance(args[2], int) and \ - args[2] < 8: - return Sub2(args[0], args[1], args[2]) - else: - raise Exception() - - -def Add(*args): - if len(args) == 3 and args[0] is SP and args[1] is SP and \ - isinstance(args[2], int) and args[2] < 256: - return AddSp(args[2]) - elif len(args) == 3 and isinstance(args[0], ArmRegister) and \ - isinstance(args[1], ArmRegister) and isinstance(args[2], int) and \ - args[2] < 8: - return Add2(args[0], args[1], args[2]) - else: - raise Exception() - - -class regregreg_base(ThumbInstruction): - """ ??? Rd, Rn, Rm """ - def __init__(self, rd, rn, rm): - self.rd = rd - self.rn = rn - self.rm = rm - - def encode(self): - at = ThumbToken() - at.rd = self.rd.num - rn = self.rn.num - rm = self.rm.num - at[3:6] = rn - at[6:9] = rm - at[9:16] = self.opcode - return at.encode() - - def __repr__(self): - return '{} {}, {}, {}'.format(self.mnemonic, self.rd, self.rn, self.rm) - - -class Add3(regregreg_base): - mnemonic = 'ADD' - opcode = 0b0001100 - - -class Sub3(regregreg_base): - mnemonic = 'SUB' - opcode = 0b0001101 - - -class Mov2(ThumbInstruction): - """ mov rd, rm """ - mnemonic = 'MOV' - def __init__(self, rd, rm): - self.rd = rd - self.rm = rm - - def encode(self): - at = ThumbToken() - at.rd = self.rd.num & 0x7 - D = (self.rd.num >> 3) & 0x1 - Rm = self.rm.num - opcode = 0b01000110 - at[8:16] = opcode - at[3:7] = Rm - at[7] = D - return at.encode() - - def __repr__(self): - return '{} {}, {}'.format(self.mnemonic, self.rd, self.rm) - - -class Mul(ThumbInstruction): - """ mul Rn, Rdm """ - mnemonic = 'MUL' - def __init__(self, rn, rdm): - self.rn = rn - self.rdm = rdm - - def encode(self): - at = ThumbToken() - rn = self.rn.num - at.rd = self.rdm.num - opcode = 0b0100001101 - #h = (opcode << 6) | (rn << 3) | rdm - at[6:16] = opcode - at[3:6] = rn - return at.encode() - - def __repr__(self): - return '{} {}, {}'.format(self.mnemonic, self.rn, self.rdm) - - -class regreg_base(ThumbInstruction): - """ ??? Rdn, Rm """ - def __init__(self, rdn, rm): - self.rdn = rdn - self.rm = rm - - def encode(self): - at = ThumbToken() - at.rd = self.rdn.num - rm = self.rm.num - at[3:6] = rm - at[6:16] = self.opcode - return at.encode() - - def __repr__(self): - mnemonic = self.__class__.__name__ - return '{} {}, {}'.format(mnemonic, self.rdn, self.rm) - - -class movregreg_ins(regreg_base): - """ mov Rd, Rm (reg8 operands) """ - opcode = 0 - - -class And(regreg_base): - opcode = 0b0100000000 - - -class Orr(regreg_base): - opcode = 0b0100001100 - - -class Cmp(regreg_base): - opcode = 0b0100001010 - - -class Lsl(regreg_base): - opcode = 0b0100000010 - - -class Cmp2(ThumbInstruction): - """ cmp Rn, imm8 """ - opcode = 5 # 00101 - def __init__(self, rn, imm): - self.rn = rn - self.imm = imm - - def encode(self): - at = ThumbToken() - at[0:8] = self.imm - at[8:11] = self.rn.num - at[11:16] = self.opcode - return at.encode() - - -# Jumping: - -class jumpBase_ins(ThumbInstruction): - def __init__(self, target_label): - assert type(target_label) is str - self.target = target_label - self.offset = 0 - - def __repr__(self): - mnemonic = self.__class__.__name__ - return '{} {}'.format(mnemonic, self.target) - - -class B(jumpBase_ins): - def encode(self): - h = (0b11100 << 11) | 0 - # | 1 # 1 to enable thumb mode - return u16(h) - - def relocations(self): - return [(self.target, 'wrap_new11')] - -class Bl(jumpBase_ins): - def encode(self): - imm11 = 0 - imm10 = 0 - j1 = 1 # TODO: what do these mean? - j2 = 1 - s = 0 - h1 = (0b11110 << 11) | (s << 10) | imm10 - h2 = (0b1101 << 12) | (j1 << 13) | (j2 << 11) | imm11 - return u16(h1) + u16(h2) - - def relocations(self): - return [(self.target, 'bl_imm11_imm10')] - - -class cond_base_ins(jumpBase_ins): - def encode(self): - imm8 = 0 - h = (0b1101 << 12) | (self.cond << 8) | imm8 - return u16(h) - - def relocations(self): - return [(self.target, 'rel8')] - - -class cond_base_ins_long(jumpBase_ins): - """ Encoding T3 """ - def encode(self): - j1 = 1 # TODO: what do these mean? - j2 = 1 - h1 = (0b11110 << 11) | (self.cond << 6) - h2 = (0b1101 << 12) | (j1 << 13) | (j2 << 11) - return u16(h1) + u16(h2) - - def relocations(self): - return [(self.target, 'b_imm11_imm6')] - - -class Beq(cond_base_ins): - cond = 0 - - -class Bne(cond_base_ins): - cond = 1 - - -class Blt(cond_base_ins): - cond = 0b1011 - - -class Bgt(cond_base_ins): - cond = 0b1100 - - -class Push(ThumbInstruction): - def __init__(self, regs): - assert type(regs) is set - self.regs = regs - - def __repr__(self): - return 'Push {{{}}}'.format(self.regs) - - def encode(self): - at = ThumbToken() - for n in register_numbers(self.regs): - if n < 8: - at[n] = 1 - elif n == 14: - at[8] = 1 - else: - raise NotImplementedError('not implemented for {}'.format(n)) - at[9:16] = 0x5a - return at.encode() - - - -def register_numbers(regs): - for r in regs: - yield r.num - -class Pop(ThumbInstruction): - def __init__(self, regs): - assert type(regs) is set - self.regs = regs - self.token = ThumbToken() - - def __repr__(self): - return 'Pop {{{}}}'.format(self.regs) - - def encode(self): - for n in register_numbers(self.regs): - if n < 8: - self.token[n] = 1 - elif n == 15: - self.token[8] = 1 - else: - raise NotImplementedError('not implemented for this register') - self.token[9:16] = 0x5E - return self.token.encode() - - - -class Yield(ThumbInstruction): - def encode(self): - return u16(0xbf10) - -# misc: - -# add/sub SP: -class addspsp_base(ThumbInstruction): - def __init__(self, imm7): - self.imm7 = imm7 - assert self.imm7 % 4 == 0 - self.imm7 >>= 2 - - def encode(self): - return u16((self.opcode << 7) | self.imm7) - - def __repr__(self): - mnemonic = self.__class__.__name__ - return '{} sp, sp, {}'.format(mnemonic, self.imm7 << 2) - - -class AddSp(addspsp_base): - opcode = 0b101100000 - - -class SubSp(addspsp_base): - opcode = 0b101100001
--- a/python/ppci/target/token.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ - -import struct - -def u16(h): - return struct.pack('<H', h) - - -def u32(x): - return struct.pack('<I', x) - - -def val2bit(v, bits): - b = [] - for i in range(bits): - b.append(bool((1<<i) & v)) - #b.reverse() - return b - - -def bit_range(b, e): - """ Property generator function """ - getter = lambda s: s[b:e] - def setter(s, v): - s[b:e] = v - return property(getter, setter) - - -class Token: - def __init__(self, bitsize): - self.bitsize = bitsize - self.bit_value = 0 - - def set_bit(self, i, value): - value = bool(value) - assert i in range(0, self.bitsize) - mask = 1 << i - if value: - self.bit_value |= mask - else: - self.bit_value &= (~mask) - - def __getitem__(self, key): - return False - - def __setitem__(self, key, value): - if type(key) is int: - self.set_bit(key, value) - elif type(key) is slice: - assert key.step is None - bits = key.stop - key.start - value_bits = val2bit(value, bits) - for i in range(key.start, key.stop): - self.set_bit(i, value_bits[i - key.start]) - else: - raise KeyError() -
--- a/python/ppci/target/x86/target_x86.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -from target import Register, Instruction, Target - -class x86Register(Register): - def __init__(self, name): - self.name = name - -class REG16(x86Register): - pass - -def addRegs(cls, names): - for name in names: - r = cls(name) - globals()[name] = r - -addRegs(REG16, ['ax', 'bx', 'cx']) - -regs = """ -ax; reg16 -""" - -class MO: - def __init__(self): - pass - -instrs = """ -add; 0x0; mem8/reg8; reg8 -""" - -# machine op table: -mot = [] - -for i in instrs.split('\n'): - i = i.strip() - if i: - print('INS:', i) - mnemonic, opcode, op1, op2 = [a.strip() for a in i.split(';')] - print(op1.split('/'), op2.split('/')) - - -print(mot) - -# Test first with these 3 instructions: -""" -mov reg64, reg64 : opcode=0x89 -xor reg64, reg64 : opcode=0x31 -inc reg64 : opcode=0xff -""" - -class x86Machine: - def __init__(self): - self.table = [] - self.table.append((0x0, 'add', 'reg8/mem8, reg8')) - self.table.append((0x1, 'add', 'reg16/mem16/reg32/mem32, reg16/reg32')) - self.table.append((0x2, 'add', 'reg8, reg8/mem8')) - def forMnemonic(self, m): - return [i for i in self.table if i[1] == m] - def emit(self, m, ops): - print(m, ops) - ops = self.forMnemonic(m) - print(ops) - - -if __name__ == '__main__': - m = x86Machine() - m.emit('add', [ax, cx]) - m.emit('mov', [bx, 1337])
--- a/python/ppci/target/x86/x86.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -import ppci -import ir - -class X86CodeGenSimple: - """ - Inefficient code generation, assume stack machine - backend - """ - def __init__(self, diag): - self.diag = diag - - def emit(self, i): - self.asm.append(i) - - def genBin(self, ir): - self.asm = [] - self.genModule(ir) - return self.asm - - def genModule(self, ir): - for f in ir.Functions: - self.genFunction(f) - def genFunction(self, f): - self.emit('global {0}'.format(f.name)) - self.emit('{0}:'.format(f.name)) - self.emit('jmp {0}'.format(f.entry.name)) - for bb in f.BasicBlocks: - self.genBB(bb) - def genBB(self, bb): - self.emit('{0}:'.format(bb.name)) - for i in bb.Instructions: - self.genIns(i) - def genIns(self, i): - if type(i) is ir.BinaryOperator: - ops = {'+':'add', '-':'sub', '*':'mul'} - if i.operation in ops: - i.result.reg = 'rax' - i.value1.reg = 'rbx' - i.value2.reg = 'rbx' - self.emit('mov {0}, {1}'.format(i.result.reg, i.value1.reg)) - self.emit('{0} {1}, {2}'.format(ops[i.operation], i.result.reg, i.value2.reg)) - else: - raise NotImplementedError('op {0}'.format(i.operation)) - elif type(i) is ir.Load: - self.emit('mov {0}, [{1}]'.format(i.value, i.location)) - elif type(i) is ir.Return: - self.emit('ret') - elif type(i) is ir.Call: - self.emit('call') - elif type(i) is ir.ImmLoad: - self.emit('mov {0}, {1}'.format(i.target, i.value)) - elif type(i) is ir.Store: - self.emit('mov [{0}], {1}'.format(i.location, i.value)) - elif type(i) is ir.ConditionalBranch: - self.emit('cmp {0}, {1}'.format(i.a, i.b)) - jmps = {'>':'jg', '<':'jl', '==':'je'} - if i.cond in jmps: - j = jmps[i.cond] - self.emit('{0} {1}'.format(j, i.lab1.name)) - else: - raise NotImplementedError('condition {0}'.format(i.cond)) - self.emit('jmp {0}'.format(i.lab2.name)) - elif type(i) is ir.Branch: - self.emit('jmp {0}'.format(i.target.name)) - elif type(i) is ir.Alloc: - pass - else: - raise NotImplementedError('{0}'.format(i)) -
--- a/python/ppci/target/x86/x86_2.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,356 +0,0 @@ -""" - X86 target descriptions and encodings. - -""" - -from target import Register, Instruction, Target, Imm8, Label, Imm3, LabelRef - - -modrm = {'rax': 0, 'rbx': 1} - -# Table 3.1 of the intel manual: -# use REX.W on the table below: -regs64 = {'rax': 0,'rcx':1,'rdx':2,'rbx':3,'rsp':4,'rbp':5,'rsi':6,'rdi':7,'r8':0,'r9':1,'r10':2,'r11':3,'r12':4,'r13':5,'r14':6,'r15':7} -regs32 = {'eax': 0, 'ecx':1, 'edx':2, 'ebx': 3, 'esp': 4, 'ebp': 5, 'esi':6, 'edi':7} -regs8 = {'al':0,'cl':1,'dl':2,'bl':3,'ah':4,'ch':5,'dh':6,'bh':7} - -# Calculation of the rexb bit: -rexbit = {'rax': 0, 'rcx':0, 'rdx':0, 'rbx': 0, 'rsp': 0, 'rbp': 0, 'rsi':0, 'rdi':0,'r8':1,'r9':1,'r10':1,'r11':1,'r12':1,'r13':1,'r14':1,'r15':1} - -# Helper functions: -def imm64(x): - """ represent 64 bits integer in little endian 8 bytes""" - if x < 0: - x = x + (1 << 64) - x = x & 0xFFFFFFFFFFFFFFFF - return [ (x >> (p*8)) & 0xFF for p in range(8) ] - -def imm32(x): - """ represent 32 bits integer in little endian 4 bytes""" - if x < 0: - x = x + (1 << 32) - x = x & 0xFFFFFFFF - return [ (x >> (p*8)) & 0xFF for p in range(4) ] - -def imm8(x): - if x < 0: - x = x + (1 << 8) - x = x & 0xFF - return [ x ] - -def modrm(mod=0, rm=0, reg=0): - """ Construct the modrm byte from its components """ - assert(mod <= 3) - assert(rm <= 7) - assert(reg <= 7) - return (mod << 6) | (reg << 3) | rm - -def rex(w=0, r=0, x=0, b=0): - """ Create a REX prefix byte """ - assert(w <= 1) - assert(r <= 1) - assert(x <= 1) - assert(b <= 1) - return 0x40 | (w<<3) | (r<<2) | (x<<1) | b - -def sib(ss=0, index=0, base=0): - assert(ss <= 3) - assert(index <= 7) - assert(base <= 7) - return (ss << 6) | (index << 3) | base - -tttn = {'L':0xc,'G':0xf,'NE':0x5,'GE':0xd,'LE':0xe, 'E':0x4} - -# Actual instructions: -def nearjump(distance, condition=None): - """ jmp imm32 """ - lim = (1<<30) - if abs(distance) > lim: - Error('near jump cannot jump over more than {0} bytes'.format(lim)) - if condition: - if distance < 0: - distance -= 6 # Skip own instruction - opcode = 0x80 | tttn[condition] # Jcc imm32 - return [0x0F, opcode] + imm32(distance) - else: - if distance < 0: - distance -= 5 # Skip own instruction - return [ 0xE9 ] + imm32(distance) - -def shortjump(distance, condition=None): - """ jmp imm8 """ - lim = 118 - if abs(distance) > lim: - Error('short jump cannot jump over more than {0} bytes'.format(lim)) - if distance < 0: - distance -= 2 # Skip own instruction - if condition: - opcode = 0x70 | tttn[condition] # Jcc rel8 - else: - opcode = 0xeb # jmp rel8 - return [opcode] + imm8(distance) - -# Helper that determines jump type: -def reljump(distance): - if abs(distance) < 110: - return shortjump(distance) - else: - return nearjump(distance) - -def push(reg): - if reg in regs64: - if rexbit[reg] == 1: - return [0x41, 0x50 + regs64[reg]] - else: - return [0x50 + regs64[reg]] - else: - Error('push for {0} not implemented'.format(reg)) - -def pop(reg): - if reg in regs64: - if rexbit[reg] == 1: - rexprefix = rex(b=1) - opcode = 0x58 + regs64[reg] - return [rexprefix, opcode] - else: - opcode = 0x58 + regs64[reg] - return [ opcode ] - else: - Error('pop for {0} not implemented'.format(reg)) - -def INT(number): - opcode = 0xcd - return [opcode] + imm8(number) - -def syscall(): - return [0x0F, 0x05] - -def call(distance): - if type(distance) is int: - return [0xe8]+imm32(distance) - elif type(distance) is str and distance in regs64: - reg = distance - opcode = 0xFF # 0xFF /2 == call r/m64 - mod_rm = modrm(mod=3, reg=2, rm=regs64[reg]) - if rexbit[reg] == 1: - rexprefix = rex(b=rexbit[reg]) - return [rexprefix, opcode, mod_rm] - else: - return [opcode, mod_rm] - else: - Error('Cannot call to {0}'.format(distance)) - -def ret(): - return [ 0xc3 ] - -def increg64(reg): - assert(reg in regs64) - rexprefix = rex(w=1, b=rexbit[reg]) - opcode = 0xff - mod_rm = modrm(mod=3, rm=regs64[reg]) - return [rexprefix, opcode, mod_rm] - -def prepost8(r8, rm8): - assert(r8 in regs8) - pre = [] - if type(rm8) is list: - # TODO: merge mem access with prepost for 64 bits - if len(rm8) == 1: - base, = rm8 - if type(base) is str and base in regs64: - assert(not base in ['rbp', 'rsp', 'r12', 'r13']) - mod_rm = modrm(mod=0, rm=regs64[base], reg=regs8[r8]) - if rexbit[base] == 1: - pre.append(rex(b=1)) - post = [mod_rm] - else: - Error('One arg of type {0} not implemented'.format(base)) - elif len(rm8) == 2: - base, offset = rm8 - assert(type(offset) is int) - assert(base in regs64) - - if base == 'rsp' or base == 'r12': - Error('Cannot use rsp or r12 as base yet') - if rexbit[base] == 1: - pre.append( rex(b=1) ) - mod_rm = modrm(mod=1, rm=regs64[base], reg=regs8[r8]) - post = [mod_rm] + imm8(offset) - else: - Error('not supporting prepost8 with list len {0}'.format(len(rm8))) - else: - Error('Not supporting move with reg8 {0}'.format(r8)) - return pre, post - -def prepost(r64, rm64): - assert(r64 in regs64) - if type(rm64) is list: - if len(rm64) == 3: - base, index, disp = rm64 - assert(base in regs64) - assert(index in regs64) - assert(type(disp) is int) - # Assert that no special cases are used: - # TODO: swap base and index to avoid special cases - # TODO: exploit special cases and make better code - assert(index != 'rsp') - - rexprefix = rex(w=1, r=rexbit[r64], x=rexbit[index], b=rexbit[base]) - # mod=1 and rm=4 indicates a SIB byte: [--][--]+imm8 - mod_rm = modrm(mod=1, rm=4, reg=regs64[r64]) - si_b = sib(ss=0, index=regs64[index], base=regs64[base]) - return [rexprefix], [mod_rm, si_b] + imm8(disp) - elif len(rm64) == 2: - base, offset = rm64 - assert(type(offset) is int) - if base == 'RIP': - # RIP pointer relative addressing mode! - rexprefix = rex(w=1, r=rexbit[r64]) - mod_rm = modrm(mod=0, rm=5, reg=regs64[r64]) - return [rexprefix], [mod_rm] + imm32(offset) - else: - assert(base in regs64) - - if base == 'rsp' or base == 'r12': - # extended function that uses SIB byte - rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[base]) - # rm=4 indicates a SIB byte follows - mod_rm = modrm(mod=1, rm=4, reg=regs64[r64]) - # index=4 indicates that index is not used - si_b = sib(ss=0, index=4, base=regs64[base]) - return [rexprefix], [mod_rm, si_b] + imm8(offset) - else: - rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[base]) - mod_rm = modrm(mod=1, rm=regs64[base], reg=regs64[r64]) - return [rexprefix], [mod_rm] + imm8(offset) - elif len(rm64) == 1: - offset = rm64[0] - if type(offset) is int: - rexprefix = rex(w=1, r=rexbit[r64]) - mod_rm = modrm(mod=0, rm=4,reg=regs64[r64]) - si_b = sib(ss=0, index=4,base=5) # 0x25 - return [rexprefix], [mod_rm, si_b] + imm32(offset) - else: - Error('Memory reference of type {0} not implemented'.format(offset)) - else: - Error('Memory reference not implemented') - elif rm64 in regs64: - rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[rm64]) - mod_rm = modrm(3, rm=regs64[rm64], reg=regs64[r64]) - return [rexprefix], [mod_rm] - -def leareg64(rega, m): - opcode = 0x8d # lea r64, m - pre, post = prepost(rega, m) - return pre + [opcode] + post - -def mov(rega, regb): - if type(regb) is int: - pre = [rex(w=1, b=rexbit[rega])] - opcode = 0xb8 + regs64[rega] - post = imm64(regb) - elif type(regb) is str: - if regb in regs64: - opcode = 0x89 # mov r/m64, r64 - pre, post = prepost(regb, rega) - elif regb in regs8: - opcode = 0x88 # mov r/m8, r8 - pre, post = prepost8(regb, rega) - else: - Error('Unknown register {0}'.format(regb)) - elif type(rega) is str: - if rega in regs64: - opcode = 0x8b # mov r64, r/m64 - pre, post = prepost(rega, regb) - else: - Error('Unknown register {0}'.format(rega)) - else: - Error('Move of this kind {0}, {1} not implemented'.format(rega, regb)) - return pre + [opcode] + post - -def xorreg64(rega, regb): - rexprefix = rex(w=1, r=rexbit[regb], b=rexbit[rega]) - opcode = 0x31 # XOR r/m64, r64 - # Alternative is 0x33 XOR r64, r/m64 - mod_rm = modrm(3, rm=regs64[rega], reg=regs64[regb]) - return [rexprefix, opcode, mod_rm] - -# integer arithmatic: -def addreg64(rega, regb): - if regb in regs64: - pre, post = prepost(regb, rega) - opcode = 0x01 # ADD r/m64, r64 - return pre + [opcode] + post - elif type(regb) is int: - if regb < 100: - rexprefix = rex(w=1, b=rexbit[rega]) - opcode = 0x83 # add r/m, imm8 - mod_rm = modrm(3, rm=regs64[rega], reg=0) - return [rexprefix, opcode, mod_rm]+imm8(regb) - elif regb < (1<<31): - rexprefix = rex(w=1, b=rexbit[rega]) - opcode = 0x81 # add r/m64, imm32 - mod_rm = modrm(3, rm=regs64[rega], reg=0) - return [rexprefix, opcode, mod_rm]+imm32(regb) - else: - Error('Constant value too large!') - else: - Error('unknown second operand!'.format(regb)) - -def subreg64(rega, regb): - if regb in regs64: - pre, post = prepost(regb, rega) - opcode = 0x29 # SUB r/m64, r64 - return pre + [opcode] + post - elif type(regb) is int: - if regb < 100: - rexprefix = rex(w=1, b=rexbit[rega]) - opcode = 0x83 # sub r/m, imm8 - mod_rm = modrm(3, rm=regs64[rega], reg=5) - return [rexprefix, opcode, mod_rm]+imm8(regb) - elif regb < (1<<31): - rexprefix = rex(w=1, b=rexbit[rega]) - opcode = 0x81 # sub r/m64, imm32 - mod_rm = modrm(3, rm=regs64[rega], reg=5) - return [rexprefix, opcode, mod_rm]+imm32(regb) - else: - Error('Constant value too large!') - - else: - Error('unknown second operand!'.format(regb)) - -def idivreg64(reg): - rexprefix = rex(w=1, b=rexbit[reg]) - opcode = 0xf7 # IDIV r/m64 - mod_rm = modrm(3, rm=regs64[reg], reg=7) - return [rexprefix, opcode, mod_rm] - -def imulreg64_rax(reg): - rexprefix = rex(w=1, b=rexbit[reg]) - opcode = 0xf7 # IMUL r/m64 - mod_rm = modrm(3, rm=regs64[reg], reg=5) - return [rexprefix, opcode, mod_rm] - -def imulreg64(rega, regb): - pre, post = prepost(rega, regb) - opcode = 0x0f # IMUL r64, r/m64 - opcode2 = 0xaf - return pre + [opcode, opcode2] + post - -def cmpreg64(rega, regb): - if regb in regs64: - pre, post = prepost(regb, rega) - opcode = 0x39 # CMP r/m64, r64 - return pre + [opcode] + post - elif type(regb) is int: - rexprefix = rex(w=1, b=rexbit[rega]) - opcode = 0x83 # CMP r/m64, imm8 - mod_rm = modrm(3, rm=regs64[rega], reg=7) - return [rexprefix, opcode, mod_rm] + imm8(regb) - - else: - Error('not implemented cmp64') - -# Mapping that maps string names to the right functions: -opcodes = {'mov':(mov,2), 'lea':(leareg64,2), 'int':(INT,1), 'syscall':(syscall,0)} -
--- a/python/ppci/tasks.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ -""" - This module defines tasks and a runner for these tasks. Tasks can - have dependencies and it can be determined if they need to be run. -""" - -import logging - -class TaskError(Exception): - def __init__(self, msg): - self.msg = msg - - -class Task: - """ Task that can run, and depend on other tasks """ - def __init__(self, name): - self.name = name - self.completed = False - self.dependencies = set() - self.duration = 1 - - def run(self): - raise NotImplementedError("Implement this abstract method!") - - def fire(self): - """ Wrapper around run that marks the task as done """ - assert all(t.completed for t in self.dependencies) - self.run() - self.completed = True - - def add_dependency(self, task): - """ Add another task as a dependency for this task """ - if task is self: - raise TaskError('Can not add dependency on task itself!') - if self in task.down_stream_tasks: - raise TaskError('Can not introduce circular task') - self.dependencies.add(task) - return task - - @property - def down_stream_tasks(self): - """ Return a set of all tasks that follow this task """ - # TODO: is this upstream or downstream??? - cdst = list(dep.down_stream_tasks for dep in self.dependencies) - cdst.append(self.dependencies) - return set.union(*cdst) - - def __gt__(self, other): - return other in self.down_stream_tasks - - def __repr__(self): - return 'Task "{}"'.format(self.name) - - -class EmptyTask(Task): - """ Basic task that does nothing """ - def run(self): - pass - - -class TaskRunner: - """ Basic task runner that can run some tasks in sequence """ - def __init__(self): - self.logger = logging.getLogger('taskrunner') - self.task_list = [] - - def add_task(self, task): - self.task_list.append(task) - - @property - def total_duration(self): - return sum(t.duration for t in self.task_list) - - def run_tasks(self): - # First sort tasks: - self.task_list.sort() - - # Run tasks: - passed_time = 0.0 - total_time = self.total_duration - try: - for t in self.task_list: - self.report_progress(passed_time / total_time, t.name) - t.fire() - passed_time += t.duration - except TaskError as e: - self.logger.error(str(e.msg)) - return 1 - self.report_progress(1, 'OK') - return 0 - - def display(self): - """ Display task how they would be run """ - for task in self.task_list: - print(task) - - def report_progress(self, percentage, text): - self.logger.info('[{:3.1%}] {}'.format(percentage, text))
--- a/python/ppci/transform.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,169 +0,0 @@ -""" - Transformation to optimize IR-code -""" - -import logging -from . import ir -# Standard passes: - -class FunctionPass: - def __init__(self): - self.logger = logging.getLogger(str(self.__class__.__name__)) - - def run(self, ir): - """ Main entry point for the pass """ - self.logger.debug('Running pass {}'.format(self.__class__.__name__)) - self.prepare() - for f in ir.Functions: - self.onFunction(f) - - def onFunction(self, f): - """ Override this virtual method """ - raise NotImplementedError() - - def prepare(self): - pass - - -class BasicBlockPass(FunctionPass): - def onFunction(self, f): - for bb in f.Blocks: - self.onBasicBlock(bb) - - def onBasicBlock(self, bb): - """ Override this virtual method """ - raise NotImplementedError() - - -class InstructionPass(BasicBlockPass): - def onBasicBlock(self, bb): - for ins in iter(bb.Instructions): - self.onInstruction(ins) - - def onInstruction(self, ins): - """ Override this virtual method """ - raise NotImplementedError() - - -class BasePass(BasicBlockPass): - def onBasicBlock(self, bb): - pass - - -# Usefull transforms: -class ConstantFolder(BasePass): - def __init__(self): - super().__init__() - self.ops = {} - self.ops['+'] = lambda x, y: x + y - self.ops['-'] = lambda x, y: x - y - self.ops['*'] = lambda x, y: x * y - self.ops['<<'] = lambda x, y: x << y - - def postExpr(self, expr): - if type(i) is BinaryOperator and i.operation in self.ops.keys() and type(i.a) is Const and type(i.b) is Const: - vr = self.ops[i.operation](i.a.value, i.b.value) - return Const(vr) - else: - return expr - - -class DeadCodeDeleter(BasicBlockPass): - def onBasicBlock(self, bb): - def instructionUsed(ins): - if not type(ins) in [ImmLoad, BinaryOperator]: - return True - if len(ins.defs) == 0: - # In case this instruction does not define any - # variables, assume it is usefull. - return True - return any(d.Used for d in ins.defs) - - change = True - while change: - change = False - for i in bb.Instructions: - if instructionUsed(i): - continue - bb.removeInstruction(i) - change = True - - -class CommonSubexpressionElimination(BasicBlockPass): - def onBasicBlock(self, bb): - constMap = {} - to_remove = [] - for i in bb.Instructions: - if isinstance(i, ImmLoad): - if i.value in constMap: - t_new = constMap[i.value] - t_old = i.target - logging.debug('Replacing {} with {}'.format(t_old, t_new)) - t_old.replaceby(t_new) - to_remove.append(i) - else: - constMap[i.value] = i.target - elif isinstance(i, BinaryOperator): - k = (i.value1, i.operation, i.value2) - if k in constMap: - t_old = i.result - t_new = constMap[k] - logging.debug('Replacing {} with {}'.format(t_old, t_new)) - t_old.replaceby(t_new) - to_remove.append(i) - else: - constMap[k] = i.result - for i in to_remove: - self.logger.debug('removing {}'.format(i)) - bb.removeInstruction(i) - -class RemoveAddZero(InstructionPass): - def onInstruction(self, i): - if type(i) is ir.Binop: - print(i) - pass - -class CleanPass(FunctionPass): - def onFunction(self, f): - self.remove_empty_blocks(f) - self.remove_one_preds(f) - - def remove_empty_blocks(self, f): - """ Remove empty basic blocks from function. """ - # If a block only contains a branch, it can be removed: - empty = lambda b: type(b.FirstInstruction) is ir.Jump - empty_blocks = list(filter(empty, f.Blocks)) - for b in empty_blocks: - # Update predecessors - preds = b.Predecessors - if b not in preds + [f.entry]: - # Do not remove if preceeded by itself - tgt = b.LastInstruction.target - for pred in preds: - pred.LastInstruction.changeTarget(b, tgt) - self.logger.debug('Removing empty block: {}'.format(b)) - f.removeBlock(b) - - def remove_one_preds(self, f): - """ Remove basic blocks with only one predecessor """ - change = True - while change: - change = False - for block in f.Blocks: - preds = block.Predecessors - if len(preds) == 1 and block not in preds and type(preds[0].LastInstruction) is ir.Jump and block is not f.epiloog: - self.glue_blocks(preds[0], block, f) - change = True - - def glue_blocks(self, block1, block2, f): - """ Glue two blocks together into the first block """ - self.logger.debug('Merging {} and {}'.format(block1.name, block2.name)) - - # Remove the last jump: - block1.removeInstruction(block1.LastInstruction) - - # Copy all instructions to block1: - for instruction in block2.Instructions: - block1.addInstruction(instruction) - # This does not work somehow: - #block2.parent.removeBlock(block2)
--- a/python/pyburg.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,340 +0,0 @@ -#!/usr/bin/python - -""" -Bottom up rewrite generator ---------------------------- - -This script takes as input a description of patterns and outputs a -matcher class that can match trees given the patterns. - -Patterns are specified as follows:: - - reg -> ADDI32(reg, reg) 2 (. add NT0 NT1 .) - reg -> MULI32(reg, reg) 3 (. .) - -or a multiply add:: - - reg -> ADDI32(MULI32(reg, reg), reg) 4 (. muladd $1, $2, $3 .) - -The general specification pattern is:: - - [result] -> [tree] [cost] [template code] - -Trees ------ - -A tree is described using parenthesis notation. For example a node X with -three child nodes is described as: - - X(a, b, b) - -Trees can be nested: - - X(Y(a, a), a) - -The 'a' in the example above indicates an open connection to a next tree -pattern. - - -In the example above 'reg' is a non-terminal. ADDI32 is a terminal. non-terminals -cannot have child nodes. A special case occurs in this case: - - reg -> rc - -where 'rc' is a non-terminal. This is an example of a chain rule. Chain rules -can be used to allow several variants of non-terminals. - -The generated matcher uses dynamic programming to find the best match of the -tree. This strategy consists of two steps: - - - label: During this phase the given tree is traversed in a bottom up way. - each node is labelled with a possible matching rule and the corresponding cost. - - select: In this step, the tree is traversed again, selecting at each point - the cheapest way to get to the goal. - -""" - -import sys -import os -import io -import types -import argparse -from ppci import Token -from pyyacc import ParserException, EOF -import yacc -import baselex -from tree import Tree - -# Generate parser on the fly: -spec_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'burg.x') -burg_parser = yacc.load_as_module(spec_file) - - -class BurgLexer: - def feed(self, txt): - tok_spec = [ - ('id', r'[A-Za-z][A-Za-z\d_]*', lambda typ, val: (typ, val)), - ('kw', r'%[A-Za-z][A-Za-z\d_]*', lambda typ, val: (val, val)), - ('number', r'\d+', lambda typ, val: (typ, int(val))), - ('STRING', r"'[^']*'", lambda typ, val: ('id', val[1:-1])), - ('template', r"\(\..*\.\)", lambda typ, val: (typ, val)), - ('OTHER', r'[:;\|\(\),]', lambda typ, val: (val, val)), - ('SKIP', r'[ ]', None) - ] - - lines = txt.split('\n') - header_lines = [] - - def tokenize(): - section = 0 - for line in lines: - line = line.strip() - if not line: - continue # Skip empty lines - elif line == '%%': - section += 1 - if section == 1: - yield Token('header', header_lines) - yield Token('%%', '%%') - else: - if section == 0: - header_lines.append(line) - else: - yield from baselex.tokenize(tok_spec, line) - yield Token(EOF, EOF) - self.tokens = tokenize() - self.token = self.tokens.__next__() - - def next_token(self): - t = self.token - if t.typ != EOF: - self.token = self.tokens.__next__() - return t - - -class Rule: - """ A rewrite rule. Specifies a tree that can be rewritten into a result - at a specific cost """ - def __init__(self, non_term, tree, cost, template): - self.non_term = non_term - self.tree = tree - self.cost = cost - self.template = template - self.nr = 0 - - def __repr__(self): - return '{} -> {} ${}'.format(self.non_term, self.tree, self.cost) - - -class Symbol: - def __init__(self, name): - self.name = name - - -class Term(Symbol): - pass - - -class Nonterm(Symbol): - def __init__(self, name): - super().__init__(name) - self.chain_rules = [] - - -class BurgSystem: - def __init__(self): - self.rules = [] - self.symbols = {} - self.goal = None - - def symType(self, t): - return (s.name for s in self.symbols.values() if type(s) is t) - - terminals = property(lambda s: s.symType(Term)) - - non_terminals = property(lambda s: s.symType(Nonterm)) - - def add_rule(self, non_term, tree, cost, template): - template = template[2:-2].strip() - if not template: - template = 'pass' - rule = Rule(non_term, tree, cost, template) - if len(tree.children) == 0 and tree.name not in self.terminals: - self.non_term(tree.name).chain_rules.append(rule) - self.non_term(rule.non_term) - self.rules.append(rule) - rule.nr = len(self.rules) - - def non_term(self, name): - if name in self.terminals: - raise BurgError('Cannot redefine terminal') - if not self.goal: - self.goal = name - return self.install(name, Nonterm) - - def tree(self, name, *args): - return Tree(name, *args) - - def install(self, name, t): - assert type(name) is str - if name in self.symbols: - assert type(self.symbols[name]) is t - else: - self.symbols[name] = t(name) - return self.symbols[name] - - def add_terminal(self, terminal): - self.install(terminal, Term) - - -class BurgError(Exception): - pass - - -class BurgParser(burg_parser.Parser): - """ Derived from automatically generated parser """ - def parse(self, l): - self.system = BurgSystem() - super().parse(l) - return self.system - - -class BurgGenerator: - def print(self, *args): - """ Print helper function that prints to output file """ - print(*args, file=self.output_file) - - def generate(self, system, output_file): - """ Generate script that implements the burg spec """ - self.output_file = output_file - self.system = system - - self.print('#!/usr/bin/python') - self.print('from tree import Tree, BaseMatcher, State') - for header in self.system.header_lines: - self.print(header) - self.print() - self.print('class Matcher(BaseMatcher):') - self.print(' def __init__(self):') - self.print(' self.kid_functions = {}') - self.print(' self.nts_map = {}') - self.print(' self.pat_f = {}') - for rule in self.system.rules: - kids, dummy = self.compute_kids(rule.tree, 't') - rule.num_nts = len(dummy) - lf = 'lambda t: [{}]'.format(', '.join(kids), rule) - pf = 'self.P{}'.format(rule.nr) - self.print(' # {}: {}'.format(rule.nr, rule)) - self.print(' self.kid_functions[{}] = {}'.format(rule.nr, lf)) - self.print(' self.nts_map[{}] = {}'.format(rule.nr, dummy)) - self.print(' self.pat_f[{}] = {}'.format(rule.nr, pf)) - self.print() - for rule in self.system.rules: - if rule.num_nts > 0: - args = ', ' + ', '.join('nt{}'.format(x) for x in range(rule.num_nts)) - else: - args = '' - self.print(' def P{}(self, tree{}):'.format(rule.nr, args)) - template = rule.template - template = template.replace('$$', 'tree') - for i in range(rule.num_nts): - template = template.replace('${}'.format(i+1), 'nt{}'.format(i)) - for t in template.split(';'): - self.print(' {}'.format(t.strip())) - self.emit_state() - self.print(' def gen(self, tree):') - self.print(' self.burm_label(tree)') - self.print(' if not tree.state.has_goal("{}"):'.format(self.system.goal)) - self.print(' raise Exception("Tree {} not covered".format(tree))') - self.print(' return self.apply_rules(tree, "{}")'.format(self.system.goal)) - - def emit_record(self, rule, state_var): - # TODO: check for rules fullfilled (by not using 999999) - self.print(' nts = self.nts({})'.format(rule.nr)) - self.print(' kids = self.kids(tree, {})'.format(rule.nr)) - self.print(' c = sum(x.state.get_cost(y) for x, y in zip(kids, nts)) + {}'.format(rule.cost)) - self.print(' tree.state.set_cost("{}", c, {})'.format(rule.non_term, rule.nr)) - for cr in self.system.symbols[rule.non_term].chain_rules: - self.print(' # Chain rule: {}'.format(cr)) - self.print(' tree.state.set_cost("{}", c + {}, {})'.format(cr.non_term, cr.cost, cr.nr)) - - def emit_state(self): - """ Emit a function that assigns a new state to a node """ - self.print(' def burm_state(self, tree):') - self.print(' tree.state = State()') - for term in self.system.terminals: - self.emitcase(term) - self.print() - - def emitcase(self, term): - rules = [rule for rule in self.system.rules if rule.tree.name == term] - for rule in rules: - condition = self.emittest(rule.tree, 'tree') - self.print(' if {}:'.format(condition)) - self.emit_record(rule, 'state') - - def compute_kids(self, t, root_name): - """ Compute of a pattern the blanks that must be provided from below in the tree """ - if t.name in self.system.non_terminals: - return [root_name], [t.name] - else: - k = [] - nts = [] - for i, c in enumerate(t.children): - pfx = root_name + '.children[{}]'.format(i) - kf, dummy = self.compute_kids(c, pfx) - nts.extend(dummy) - k.extend(kf) - return k, nts - - - def emittest(self, tree, prefix): - """ Generate condition for a tree pattern """ - ct = (c for c in tree.children if c.name not in self.system.non_terminals) - child_tests = (self.emittest(c, prefix + '.children[{}]'.format(i)) for i, c in enumerate(ct)) - child_tests = ('({})'.format(ct) for ct in child_tests) - child_tests = ' and '.join(child_tests) - child_tests = ' and ' + child_tests if child_tests else '' - tst = '{}.name == "{}"'.format(prefix, tree.name) - return tst + child_tests - - -def make_argument_parser(): - """ Constructs an argument parser """ - parser = argparse.ArgumentParser(description='pyburg bottom up rewrite system generator compiler compiler') - parser.add_argument('source', type=argparse.FileType('r'), \ - help='the parser specification') - parser.add_argument('-o', '--output', type=argparse.FileType('w'), \ - default=sys.stdout) - return parser - -def load_as_module(filename): - """ Load a parser spec file, generate LR tables and create module """ - ob = io.StringIO() - args = argparse.Namespace(source=open(filename), output=ob) - main(args) - - matcher_mod = types.ModuleType('generated_matcher') - exec(ob.getvalue(), matcher_mod.__dict__) - return matcher_mod - - -def main(args): - src = args.source.read() - args.source.close() - - # Parse specification into burgsystem: - l = BurgLexer() - p = BurgParser() - l.feed(src) - burg_system = p.parse(l) - - # Generate matcher: - generator = BurgGenerator() - generator.generate(burg_system, args.output) - - -if __name__ == '__main__': - # Parse arguments: - args = make_argument_parser().parse_args() - main(args)
--- a/python/pyyacc.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,405 +0,0 @@ -""" - Parser generator script -""" - -from ppci import Token - -EPS = 'EPS' -EOF = 'EOF' - - -class ParserGenerationException(Exception): - """ Raised when something goes wrong during parser generation """ - pass - - -class ParserException(Exception): - """ Raised during a failure in the parsing process """ - pass - - -class Action: - def __repr__(self): - return 'Action' - - def __eq__(self, other): - return str(self) == str(other) - - -class Shift(Action): - def __init__(self, to_state): - self.to_state = to_state - - def __repr__(self): - return 'Shift({})'.format(self.to_state) - - -class Reduce(Action): - def __init__(self, rule): - self.rule = rule - - def __repr__(self): - return 'Reduce({})'.format(self.rule) - - -class Accept(Reduce): - def __repr__(self): - return 'Accept({})'.format(self.rule) - - -def print_grammar(g): - """ Pretty print a grammar """ - print(g) - for production in g.productions: - print(production) - - -def calculate_first_sets(grammar): - """ - Calculate first sets for each grammar symbol - This is a dictionary which maps each grammar symbol - to a set of terminals that can be encountered first - when looking for the symbol. - """ - first = {} - nullable = {} - for terminal in grammar.terminals | {EOF, EPS}: - first[terminal] = set([terminal]) - nullable[terminal] = False - for nt in grammar.nonterminals: - first[nt] = set() - nullable[nt] = False - while True: - some_change = False - for rule in grammar.productions: - # Check for null-ability: - if all(nullable[beta] for beta in rule.symbols): - if not nullable[rule.name]: - nullable[rule.name] = True - some_change = True - # Update first sets: - for beta in rule.symbols: - if not nullable[beta]: - if first[beta] - first[rule.name]: - first[rule.name] |= first[beta] - some_change = True - break - if not some_change: - break - return first - - -class Grammar: - """ Defines a grammar of a language """ - def __init__(self, terminals): - self.terminals = set(terminals) - self.nonterminals = set() - self.productions = [] - self._first = None # Cached first set - self.start_symbol = None - - def __repr__(self): - return 'Grammar with {} rules'.format(len(self.productions)) - - def add_production(self, name, symbols, f=None): - """ Add a production rule to the grammar """ - production = Production(name, symbols, f) - self.productions.append(production) - if name in self.terminals: - raise ParserGenerationException("Cannot redefine terminal {0}".format(name)) - self.nonterminals.add(name) - self._first = None # Invalidate cached version - - def productionsForName(self, name): - """ Retrieve all productions for a non terminal """ - return [p for p in self.productions if p.name == name] - - @property - def Symbols(self): - """ Get all the symbols defined by this grammar """ - return self.nonterminals | self.terminals - - @property - def first(self): - """ - The first set is a mapping from a grammar symbol to a set of - set of all terminal symbols that can be the first terminal when - looking for the grammar symbol - """ - if not self._first: - self._first = calculate_first_sets(self) - return self._first - - def closure(self, itemset): - """ Expand itemset by using epsilon moves """ - worklist = list(itemset) - def addIt(itm): - if not itm in itemset: - itemset.add(itm) - worklist.append(itm) - def first2(itm): - # When using the first sets, create a copy: - f = set(self.first[itm.NextNext]) - if EPS in f: - f.discard(EPS) - f.add(itm.look_ahead) - return f - # Start of algorithm: - while worklist: - item = worklist.pop(0) - if not item.IsShift: - continue - if not (item.Next in self.nonterminals): - continue - C = item.Next - for add_p in self.productionsForName(C): - for b in first2(item): - addIt(Item(add_p, 0, b)) - return frozenset(itemset) - - def initialItemSet(self): - """ Calculates the initial item set """ - iis = set() - for p in self.productionsForName(self.start_symbol): - iis.add(Item(p, 0, EOF)) - return self.closure(iis) - - def nextItemSet(self, itemset, symbol): - """ - Determines the next itemset for the current set and a symbol - This is the goto procedure - """ - next_set = set() - for item in itemset: - if item.can_shift_over(symbol): - next_set.add(item.shifted()) - return self.closure(next_set) - - def genCanonicalSet(self, iis): - states = [] - worklist = [] - transitions = {} - def addSt(s): - if not (s in states): - worklist.append(s) - states.append(s) - addSt(iis) - while len(worklist) > 0: - itemset = worklist.pop(0) - for symbol in self.Symbols: - nis = self.nextItemSet(itemset, symbol) - if not nis: - continue - addSt(nis) - transitions[(states.index(itemset), symbol)] = states.index(nis) - return states, transitions - - def checkSymbols(self): - """ Checks no symbols are undefined """ - for production in self.productions: - for symbol in production.symbols: - if symbol not in self.Symbols: - raise ParserGenerationException('Symbol {0} undefined'.format(symbol)) - - def generate_parser(self): - """ Generates a parser from the grammar """ - action_table, goto_table = self.generate_tables() - p = LRParser(action_table, goto_table, self.start_symbol) - p.grammar = self - return p - - def generate_tables(self): - """ Generate parsing tables """ - if not self.start_symbol: - self.start_symbol = self.productions[0].name - self.checkSymbols() - action_table = {} - goto_table = {} - iis = self.initialItemSet() - - # First generate all item sets by using the nextItemset function: - states, transitions = self.genCanonicalSet(iis) - - def setAction(state, t, action): - assert isinstance(action, Action) - key = (state, t) - assert type(state) is int - assert type(t) is str - if key in action_table: - action2 = action_table[key] - if action != action2: - if (type(action2) is Reduce) and (type(action) is Shift): - # Automatically resolve and do the shift action! - # Simple, but almost always what you want!! - action_table[key] = action - elif isinstance(action2, Shift) and isinstance(action, Reduce): - pass - else: - a1 = str(action) - a2 = str(action2) - raise ParserGenerationException('LR construction conflict {0} vs {1}'.format(a1, a2)) - else: - action_table[key] = action - - # Fill action table: - for state in states: - # Detect conflicts: - for item in state: - if item.IsShift and item.Next in self.terminals: - # Rule 1, a shift item: - nextstate = transitions[(states.index(state), item.Next)] - setAction(states.index(state), item.Next, Shift(nextstate)) - if item.IsReduce: - if item.production.name == self.start_symbol and item.look_ahead == EOF: - # Rule 3: accept: - act = Accept(self.productions.index(item.production)) - else: - # Rule 2, reduce item: - act = Reduce(self.productions.index(item.production)) - setAction(states.index(state), item.look_ahead, act) - for nt in self.nonterminals: - key = (states.index(state), nt) - if key in transitions: - goto_table[key] = transitions[key] - return action_table, goto_table - - -class Production: - """ Production rule for a grammar """ - def __init__(self, name, symbols, f): - self.name = name - self.symbols = symbols - self.f = f - - def __repr__(self): - action = ' ' + str(self.f) if self.f else '' - return '{0} -> {1}'.format(self.name, self.symbols) + action - - -class Item: - """ - Represents a partially parsed item - It has a production it is looking for, a position - in this production called the 'dot' and a look ahead - symbol that must follow this item. - """ - def __init__(self, production, dotpos, look_ahead): - self.production = production - self.dotpos = dotpos - assert self.dotpos <= len(self.production.symbols) - self.look_ahead = look_ahead - self._is_shift = self.dotpos < len(self.production.symbols) - - def getdata(self): - """ Gets the members as a tuple """ - return (self.production, self.dotpos, self.look_ahead) - - def __eq__(self, other): - if type(other) is type(self): - return self.getdata() == other.getdata() - return False - - def __hash__(self): - return self.getdata().__hash__() - - @property - def IsReduce(self): - """ Check if this item has the dot at the end """ - return not self._is_shift - - @property - def IsShift(self): - """ Check if this item is a shift item, i.e. the dot can proceed """ - return self._is_shift - - @property - def Next(self): - """ Returns the symbol after the dot """ - return self.production.symbols[self.dotpos] - - def can_shift_over(self, symbol): - """ Determines if this item can shift over the given symbol """ - return self.IsShift and self.Next == symbol - - def shifted(self): - """ Creates a new item that is shifted one position """ - return Item(self.production, self.dotpos + 1, self.look_ahead) - - @property - def NextNext(self): - """ Gets the symbol after the next symbol, or EPS if at the end """ - if self.dotpos + 1 >= len(self.production.symbols): - return EPS - else: - return self.production.symbols[self.dotpos + 1] - - def __repr__(self): - prod = self.production - predot = ' '.join(prod.symbols[0:self.dotpos]) - postdot = ' '.join(prod.symbols[self.dotpos:]) - name = prod.name - args = (name, predot, postdot, self.look_ahead) - return '[{0} -> {1} . {2} -> {3}]'.format(*args) - - -class LRParser: - """ LR parser automata """ - def __init__(self, action_table, goto_table, start_symbol): - self.action_table = action_table - self.goto_table = goto_table - self.start_symbol = start_symbol - - def parse(self, lexer): - """ Parse an iterable with tokens """ - assert hasattr(lexer, 'next_token'), '{0} is no lexer'.format(type(lexer)) - stack = [0] - r_data_stack = [] - look_ahead = lexer.next_token() - assert type(look_ahead) is Token - # TODO: exit on this condition: - while stack != [0, self.start_symbol, 2222]: - state = stack[-1] # top of stack - key = (state, look_ahead.typ) - if not key in self.action_table: - raise ParserException('Error parsing at character {0}'.format(look_ahead)) - action = self.action_table[key] - if type(action) is Reduce: - f_args = [] - prod = self.grammar.productions[action.rule] - for s in prod.symbols: - stack.pop() - stack.pop() - f_args.append(r_data_stack.pop()) - f_args.reverse() - r_data = None - if prod.f: - r_data = prod.f(*f_args) - state = stack[-1] - stack.append(prod.name) - stack.append(self.goto_table[(state, prod.name)]) - r_data_stack.append(r_data) - elif type(action) is Shift: - stack.append(look_ahead.typ) - stack.append(action.to_state) - r_data_stack.append(look_ahead) - look_ahead = lexer.next_token() - assert type(look_ahead) is Token - elif type(action) is Accept: - # Pop last rule data off the stack: - f_args = [] - param = self.grammar.productions[action.rule] - for s in param.symbols: - stack.pop() - stack.pop() - f_args.append(r_data_stack.pop()) - f_args.reverse() - if param.f: - ret_val = param.f(*f_args) - else: - ret_val = None - # Break out! - break - # At exit, the stack must be 1 long - # TODO: fix that this holds: - #assert len(stack) == 1, 'stack {0} not totally reduce'.format(stack) - return ret_val
--- a/python/tree.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ - -class Tree: - """ Tree node with a name and possibly some child nodes """ - def __init__(self, name, *args): - self.name = name - self.value = None - self.children = args - - def __repr__(self): - if self.children: - ch = ', '.join(str(c) for c in self.children) - return '{}({})'.format(self.name, ch) - else: - return '{}'.format(self.name) - - -class State: - """ State used to label tree nodes """ - def __init__(self): - self.labels = {} - - def has_goal(self, goal): - return goal in self.labels - - def get_cost(self, goal): - return self.labels[goal][0] - - def get_rule(self, goal): - return self.labels[goal][1] - - def set_cost(self, goal, cost, rule): - if self.has_goal(goal): - if self.get_cost(goal) > cost: - self.labels[goal] = (cost, rule) - else: - self.labels[goal] = (cost, rule) - - -class BaseMatcher: - """ Base class for matcher objects. """ - def kids(self, tree, rule): - return self.kid_functions[rule](tree) - - def nts(self, rule): - return self.nts_map[rule] - - def burm_label(self, tree): - """ Label all nodes in the tree bottom up """ - for c in tree.children: - self.burm_label(c) - self.burm_state(tree) - - def apply_rules(self, tree, goal): - rule = tree.state.get_rule(goal) - results = [self.apply_rules(kid_tree, kid_goal) - for kid_tree, kid_goal in zip(self.kids(tree, rule), self.nts(rule))] - return self.pat_f[rule](tree, *results)
--- a/python/utils/__init__.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ - - -from .hexfile import HexFile, HexFileException -
--- a/python/utils/adi.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ - -# Implementation of the ADI (ARM Debug Interface) v5 interface. - -COMPONENT_CLASSES = {0x1: 'ROM table'} - -class Adi: - def __init__(self, iface): - self.iface = iface - def r32(self, address): - return self.iface.read_debug32(address) - def w32(self, address, value): - self.iface.write_debug32(address, value) - def getId(self, offset): - print('reading id from {0:X}'.format(offset)) - pid4 = self.r32(offset + 0xFD0) - #print('pid4', pid4) - pid5 = self.r32(offset + 0xFD4) - pid6 = self.r32(offset + 0xFD8) - pid7 = self.r32(offset + 0xFDC) - pid0 = self.r32(offset + 0xFE0) - pid1 = self.r32(offset + 0xFE4) - pid2 = self.r32(offset + 0xFE8) - pid3 = self.r32(offset + 0xFEC) - cid0 = self.r32(offset + 0xFF0) - cid1 = self.r32(offset + 0xFF4) - cid2 = self.r32(offset + 0xFF8) - cid3 = self.r32(offset + 0xFFC) - pids = [pid0, pid1, pid2, pid3, pid4, pid5, pid6, pid7] - cids = [cid0, cid1, cid2, cid3] - print('cids:', [hex(x) for x in cids], 'pids', [hex(x) for x in pids]) - valid = cid0 == 0xD and (cid1 & 0xF) == 0x0 and cid2 == 0x5 and cid3 == 0xB1 - if valid: - component_class = cid1 >> 4 - else: - print('invalid class') - component_class = 0 - # TODO: use pids - return component_class, pids - - def parseRomTable(self, offset): - assert (offset & 0xFFF) == 0 - component_class, pid = self.getId(offset) - assert component_class == 1 - print('Component class:', COMPONENT_CLASSES[component_class]) - print('memory also on this bus:', self.r32(offset + 0xFCC)) - idx = 0 - entry = self.r32(offset + idx * 4) - while entry != 0: - #print('Entry: {0:X}'.format(entry)) - entryOffset = entry & 0xFFFFF000 - cls, pids = self.getId((offset + entryOffset) & 0xFFFFFFFF) - print('class:', cls) - if cls == 9: - print('Debug block found!') - - idx += 1 - entry = self.r32(offset + idx * 4) - -
--- a/python/utils/devices.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -import sys -import usb - -# Global device list to which devices are registered. -devices = {} - -def registerDevice(chipId): - """ Decorator to register a device """ - def wrapper(dev): - devices[chipId] = dev - return dev - return wrapper - -# Global interface dictionary. -interfaces = {} - -def registerInterface(vid_pid): - def wrapper(iface): - interfaces[vid_pid] = iface - return iface - return wrapper - -def createInterfaces(): - """ Create a list of detected interfaces """ - ctx = usb.UsbContext() - - # Retrieve all usb devices: - devs = ctx.DeviceList - keys = interfaces.keys() - - # Filter function to filter only registered interfaces: - def filt(usbiface): - return (usbiface.VendorId, usbiface.ProductId) in keys - def buildInterface(usbiface): - key = (usbiface.VendorId, usbiface.ProductId) - iface = interfaces[key] - return iface(usbiface) - return [buildInterface(uif) for uif in filter(filt, devs)] - -class Device: - """ - Base class for a device possibly connected via an interface. - """ - def __init__(self, iface): - # Store the interface through which this device is connected: - assert isinstance(iface, Interface) - self.iface = iface - -class Interface: - """ - Generic interface class. Connected via Usb to a JTAG interface. - Possibly is connected with a certain chip. - """ - def createDevice(self): - """ Try to get the device connected to this interface """ - if self.ChipId in devices: - return devices[self.ChipId](self) - raise STLinkException('No device found!') - -class STLinkException(Exception): - """ Exception used for interfaces and devices """ - pass -
--- a/python/utils/hexfile.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,158 +0,0 @@ -import os -import struct -import binascii - -DATA = 0 -EOF = 1 -EXTLINADR = 4 - -class HexFileException(Exception): - pass - - -def parseHexLine(line): - """ Parses a hexfile line into three parts """ - line = line[1:] # Remove ':' - nums = bytes.fromhex(line) - bytecount = nums[0] - if len(nums) != bytecount + 5: - raise HexFileException('byte count field incorrect') - crc = sum(nums) - if (crc & 0xFF) != 0: - raise HexFileException('crc incorrect') - address = struct.unpack('>H', nums[1:3])[0] - typ = nums[3] - data = nums[4:-1] - return (address, typ, data) - -def makeHexLine(address, typ, data=bytes()): - bytecount = len(data) - nums = bytearray() - nums.append(bytecount) - nums.extend(struct.pack('>H', address)) - nums.append(typ) - nums.extend(data) - crc = sum(nums) - crc = ((~crc) + 1) & 0xFF - nums.append(crc) - line = ':' + binascii.hexlify(nums).decode('ascii') - return line - -def chunks(data, csize=16): - idx = 0 - while idx < len(data): - s = min(len(data) - idx, csize) - yield data[idx:idx+s] - idx += s - -def hexfields(f): - for line in f: - line = line.strip() # Strip spaces and newlines - if not line: - continue # Skip empty lines - if line[0] != ':': - continue # Skip lines that do not start with a ':' - yield parseHexLine(line) - - -class HexFile: - """ Represents an intel hexfile """ - def __init__(self): - self.regions = [] - self.startAddress = 0 - - def load(self, f): - endOfFile = False - ext = 0 - for address, typ, data in hexfields(f): - if endOfFile: - raise HexFileException('hexfile line after end of file record') - if typ == 0x0: # Data record - self.addRegion(address + ext, data) - elif typ == EXTLINADR: # Extended linear address record - ext = (struct.unpack('>H', data[0:2])[0]) << 16 - elif typ == EOF: # End of file record - if len(data) != 0: - raise HexFileException('end of file not empty') - endOfFile = True - elif typ == 0x5: # Start address record (where IP goes after loading) - self.startAddress = struct.unpack('>I', data[0:4])[0] - else: - raise HexFileException('record type {0} not implemented'.format(typ)) - - def __repr__(self): - size = sum(r.Size for r in self.regions) - return 'Hexfile containing {} bytes'.format(size) - - def dump(self): - print(self) - for r in self.regions: - print(r) - - def __eq__(self, other): - regions = self.regions - oregions = other.regions - if len(regions) != len(oregions): - return False - return all(rs == ro for rs, ro in zip(regions, oregions)) - - def addRegion(self, address, data): - r = HexFileRegion(address, data) - self.regions.append(r) - self.check() - - def check(self): - self.regions.sort(key=lambda r: r.address) - change = True - while change and len(self.regions) > 1: - change = False - for r1, r2 in zip(self.regions[:-1], self.regions[1:]): - if r1.EndAddress == r2.address: - r1.addData(r2.data) - self.regions.remove(r2) - change = True - elif r1.EndAddress > r2.address: - raise HexFileException('Overlapping regions') - - def merge(self, other): - for r in other.regions: - self.addRegion(r.address, r.data) - - def save(self, f): - def emit(address, typ, data=bytes()): - print(makeHexLine(address, typ, data), file=f) - for r in self.regions: - ext = r.address & 0xFFFF0000 - emit(0, EXTLINADR, struct.pack('>H', ext >> 16)) - address = r.address - ext - for chunk in chunks(r.data): - if address >= 0x10000: - ext += 0x10000 - emit(0, EXTLINADR, struct.pack('>H', ext >> 16)) - address -= 0x10000 - emit(address, DATA, chunk) - address += len(chunk) - emit(0, EOF) - - -class HexFileRegion: - def __init__(self, address, data = bytes()): - self.address = address - self.data = data - - def __repr__(self): - return 'Region at 0x{:08X} of {} bytes'.format(self.address, len(self.data)) - - def __eq__(self, other): - return (self.address, self.data) == (other.address, other.data) - - def addData(self, d): - self.data = self.data + d - - @property - def Size(self): - return len(self.data) - - @property - def EndAddress(self): - return self.address + len(self.data)
--- a/python/utils/iso9660.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -#!/usr/bin/env python - -import argparse - -__doc__ = """ - ISO 9660 filesystem utility. -""" - - -class VolumeDescriptor: - @classmethod - def FromData(cls, d): - ty = d[0] - Id = d[1:6] - assert Id == 'CD001'.encode('ascii') - ver = d[6] - assert ver == 1 - cls = vol_desc_types[ty] - return cls(d) - - -vol_desc_types = {} -def vol_type(t): - def reg_func(cls): - vol_desc_types[t] = cls - return cls - return reg_func - - -@vol_type(0) -class BootRecordVolumeDescriptor(VolumeDescriptor): - def __init__(self, d): - boot_sys_id = d[7:39] - boot_id = d[39:71] - print(boot_sys_id) - print(boot_id) - - -@vol_type(1) -class PrimaryVolumeDescriptor(VolumeDescriptor): - def __init__(self, d): - sys_id = d[8:40] - vol_id = d[40:72] - print(sys_id) - print(vol_id) - - -@vol_type(255) -class VolumeDescriptorTerminator(VolumeDescriptor): - def __init__(self, d): - pass - - -class ISOfs: - def __init__(self): - self.vol_descriptors = [] - - def read(self, f): - # System area: - self.system_area = f.read(16 * 2048) - while True: - d = f.read(2048) - desc = VolumeDescriptor.FromData(d) - self.vol_descriptors.append(desc) - if type(desc) is VolumeDescriptorTerminator: - break - - def dump(self): - for vd in self.vol_descriptors: - print(vd) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('filename') - args = parser.parse_args() - fs = ISOfs() - with open(args.filename, 'rb') as f: - fs.read(f) - fs.dump() - -
--- a/python/utils/lsusb.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -#!/usr/bin/python - -from usb import UsbContext - -# try to read usb.ids: -vids = {} -pids = {} -try: - with open('usb.ids', 'r', errors='ignore') as f: - vid = 0 - for l in f: - if l.startswith('#') or not l.strip(): - continue - if l.startswith('\t\t'): - print('iface:', l) - elif l.startswith('\t'): - print('product', l) - pid = int(l[1:5], 16) - print('product', hex(pid), l) - else: - print('vendor', l) - vid = int(l[0:4], 16) - print('vendor', hex(vid), l) - -except IOError as e: - print("Error loading usb id's: {0}".format(e)) - -context = UsbContext() -for d in context.DeviceList: - print(d) -
--- a/python/utils/st-flash.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -#!/usr/bin/python - -import argparse, sys -import stlink, stm32 -import hexfile - -def hex2int(s): - if s.startswith('0x'): - s = s[2:] - return int(s, 16) - raise ValueError('Hexadecimal value must begin with 0x') - -parser = argparse.ArgumentParser( - description='ST-link flash utility by Windel Bouwman') -subparsers = parser.add_subparsers(title='commands', - description='possible commands', dest='command') - -readparser = subparsers.add_parser('read', help='read flash contents') -readparser.add_argument('filename', type=argparse.FileType('wb', 0)) -readparser.add_argument('address', type=hex2int) -readparser.add_argument('size', type=hex2int) - -writeparser = subparsers.add_parser('write', help='write flash contents') -writeparser.add_argument('filename', type=argparse.FileType('rb')) -writeparser.add_argument('address', type=hex2int) - -hexwriteparser = subparsers.add_parser('hexwrite', help='write hexfile to flash') -hexwriteparser.add_argument('hexfile', type=argparse.FileType('r')) - -verifyparser = subparsers.add_parser('verify', help='verify flash contents') -verifyparser.add_argument('filename', type=argparse.FileType('rb')) -verifyparser.add_argument('address', type=hex2int) - -eraseparser = subparsers.add_parser('erase', help='erase flash contents') - -args = parser.parse_args() -if not args.command: - parser.print_usage() - sys.exit(1) - -# In any command case, open a device: -stl = stlink.STLink2() -stl.open() - -# Enter the right mode: -if stl.CurrentMode == stlink.DFU_MODE: - stl.exitDfuMode() - -if stl.CurrentMode != stlink.DEBUG_MODE: - stl.enterSwdMode() - -if stl.ChipId != 0x10016413: - print('Only working on stm32f4discovery board for now.') - sys.exit(2) - -# Retrieve the connected device, if any: -dev = stl.createDevice() - -if args.command == 'read': - dev_content = dev.readFlash(args.address, args.size) - args.filename.write(dev_content) -elif args.command == 'write': - content = args.filename.read() - dev.writeFlash(args.address, content) -elif args.command == 'hexwrite': - hf = hexfile.HexFile() - hf.load(args.hexfile) - r = hf.regions[0] - dev.writeFlash(r.address, r.data) -elif args.command == 'verify': - content = args.filename.read() - dev.verifyFlash(args.address, content) -elif args.command == 'erase': - dev.eraseFlash() -else: - print('unknown command', args.command) - -stl.reset() -stl.run() -stl.exitDebugMode() -
--- a/python/utils/stlink.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,367 +0,0 @@ -import struct, time -from usb import UsbContext, UsbDevice -from devices import Interface, STLinkException, registerInterface -import adi - -""" - More or less copied from: - https://github.com/texane/stlink - Tracing from: - https://github.com/obe1line/stlink-trace - -""" -ST_VID, STLINK2_PID = 0x0483, 0x3748 - -def checkDevice(device): - return device.VendorId == ST_VID and device.ProductId == STLINK2_PID - -DFU_MODE, MASS_MODE, DEBUG_MODE = 0, 1, 2 - -CORE_RUNNING = 0x80 -CORE_HALTED = 0x81 - -# Commands: -GET_VERSION = 0xf1 -DEBUG_COMMAND = 0xf2 -DFU_COMMAND = 0xf3 -GET_CURRENT_MODE = 0xf5 - -# dfu commands: -DFU_EXIT = 0x7 - -# debug commands: -DEBUG_ENTER = 0x20 -DEBUG_EXIT = 0x21 -DEBUG_ENTER_SWD = 0xa3 -DEBUG_GETSTATUS = 0x01 -DEBUG_FORCEDEBUG = 0x02 -DEBUG_RESETSYS = 0x03 -DEBUG_READALLREGS = 0x04 -DEBUG_READREG = 0x5 -DEBUG_WRITEREG = 0x6 -DEBUG_READMEM_32BIT = 0x7 -DEBUG_WRITEMEM_32BIT = 0x8 -DEBUG_RUNCORE = 0x9 -DEBUG_STEPCORE = 0xa - -JTAG_WRITEDEBUG_32BIT = 0x35 -JTAG_READDEBUG_32BIT = 0x36 -TRACE_GET_BYTE_COUNT = 0x42 - -# cortex M3 -CM3_REG_CPUID = 0xE000ED00 - -@registerInterface((ST_VID, STLINK2_PID)) -class STLink2(Interface): - """ STlink2 interface implementation. """ - def __init__(self, stlink2=None): - self.devHandle = None - if not stlink2: - context = UsbContext() - stlink2s = list(filter(checkDevice, context.DeviceList)) - if not stlink2s: - raise STLinkException('Could not find an ST link 2 interface') - if len(stlink2s) > 1: - print('More then one stlink2 found, picking first one') - stlink2 = stlink2s[0] - assert isinstance(stlink2, UsbDevice) # Nifty type checking - assert checkDevice(stlink2) - self.stlink2 = stlink2 - def __del__(self): - if self.IsOpen: - if self.CurrentMode == DEBUG_MODE: - self.exitDebugMode() - self.close() - def __str__(self): - if self.IsOpen: - return 'STlink2 device version {0}'.format(self.Version) - else: - return 'STlink2 device' - def open(self): - if self.IsOpen: - return - self.devHandle = self.stlink2.open() - if self.devHandle.Configuration != 1: - self.devHandle.Configuration = 1 - self.devHandle.claimInterface(0) - - # First initialization: - if self.CurrentMode == DFU_MODE: - self.exitDfuMode() - if self.CurrentMode != DEBUG_MODE: - self.enterSwdMode() - #self.reset() - def close(self): - if self.IsOpen: - self.devHandle.close() - self.devHandle = None - @property - def IsOpen(self): - return self.devHandle != None - # modes: - def getCurrentMode(self): - cmd = bytearray(16) - cmd[0] = GET_CURRENT_MODE - reply = self.send_recv(cmd, 2) # Expect 2 bytes back - return reply[0] - CurrentMode = property(getCurrentMode) - @property - def CurrentModeString(self): - modes = {DFU_MODE: 'dfu', MASS_MODE: 'massmode', DEBUG_MODE:'debug'} - return modes[self.CurrentMode] - def exitDfuMode(self): - cmd = bytearray(16) - cmd[0:2] = DFU_COMMAND, DFU_EXIT - self.send_recv(cmd) - def enterSwdMode(self): - cmd = bytearray(16) - cmd[0:3] = DEBUG_COMMAND, DEBUG_ENTER, DEBUG_ENTER_SWD - self.send_recv(cmd) - def exitDebugMode(self): - cmd = bytearray(16) - cmd[0:2] = DEBUG_COMMAND, DEBUG_EXIT - self.send_recv(cmd) - - def getVersion(self): - cmd = bytearray(16) - cmd[0] = GET_VERSION - data = self.send_recv(cmd, 6) # Expect 6 bytes back - # Parse 6 bytes into various versions: - b0, b1, b2, b3, b4, b5 = data - stlink_v = b0 >> 4 - jtag_v = ((b0 & 0xf) << 2) | (b1 >> 6) - swim_v = b1 & 0x3f - vid = (b3 << 8) | b2 - pid = (b5 << 8) | b4 - return 'stlink={0} jtag={1} swim={2} vid:pid={3:04X}:{4:04X}'.format(\ - stlink_v, jtag_v, swim_v, vid, pid) - Version = property(getVersion) - - @property - def ChipId(self): - return self.read_debug32(0xE0042000) - @property - def CpuId(self): - u32 = self.read_debug32(CM3_REG_CPUID) - implementer_id = (u32 >> 24) & 0x7f - variant = (u32 >> 20) & 0xf - part = (u32 >> 4) & 0xfff - revision = u32 & 0xf - return implementer_id, variant, part, revision - def getStatus(self): - cmd = bytearray(16) - cmd[0:2] = DEBUG_COMMAND, DEBUG_GETSTATUS - reply = self.send_recv(cmd, 2) - return reply[0] - Status = property(getStatus) - @property - def StatusString(self): - s = self.Status - statii = {CORE_RUNNING: 'CORE RUNNING', CORE_HALTED: 'CORE HALTED'} - if s in statii: - return statii[s] - return 'Unknown status' - - def reset(self): - cmd = bytearray(16) - cmd[0:2] = DEBUG_COMMAND, DEBUG_RESETSYS - self.send_recv(cmd, 2) - - # debug commands: - def step(self): - cmd = bytearray(16) - cmd[0:2] = DEBUG_COMMAND, DEBUG_STEPCORE - self.send_recv(cmd, 2) - def run(self): - cmd = bytearray(16) - cmd[0:2] = DEBUG_COMMAND, DEBUG_RUNCORE - self.send_recv(cmd, 2) - def halt(self): - cmd = bytearray(16) - cmd[0:2] = DEBUG_COMMAND, DEBUG_FORCEDEBUG - self.send_recv(cmd, 2) - - # Tracing: - def traceEnable(self): - self.write_debug32(0xE000EDF0, 0xA05F0003) - - # Enable TRCENA: - DEMCR = 0xE000EDFC - v = self.read_debug32(DEMCR) - v |= (1 << 24) - self.write_debug32(DEMCR, v) - - # ?? Enable write?? - self.write_debug32(0xE0002000, 0x2) # - - # DBGMCU_CR: - self.write_debug32(0xE0042004, 0x27) # Enable trace in async mode - - # TPIU config: - self.write_debug32(0xE0040004, 0x00000001) # current port size register --> 1 == port size = 1 - self.write_debug32(0xE0040010, 0x23) # random clock divider?? - self.write_debug32(0xE00400F0, 0x2) # selected pin protocol (2 == NRZ) - self.write_debug32(0xE0040304, 0x100) # continuous formatting - - # ITM config: - self.write_debug32(0xE0000FB0, 0xC5ACCE55) # Unlock write access to ITM - self.write_debug32(0xE0000F80, 0x00010005) # ITM Enable, sync enable, ATB=1 - self.write_debug32(0xE0000E00, 0xFFFFFFFF) # Enable all trace ports in ITM - self.write_debug32(0xE0000E40, 0x0000000F) # Set privilege mask for all 32 ports. - def writePort0(self, v32): - self.write_debug32(0xE0000000, v32) - def getTraceByteCount(self): - cmd = bytearray(16) - cmd[0:2] = DEBUG_COMMAND, 0x42 - reply = self.send_recv(cmd, 2) - return struct.unpack('<H', reply[0:2])[0] - def readTraceData(self): - bsize = self.getTraceByteCount() - if bsize > 0: - td = self.recv_ep3(bsize) - print(td) - else: - print('no trace data') - - # Helper 1 functions: - def write_debug32(self, address, value): - cmd = bytearray(16) - cmd[0:2] = DEBUG_COMMAND, JTAG_WRITEDEBUG_32BIT - cmd[2:10] = struct.pack('<II', address, value) - r = self.send_recv(cmd, 2) - def read_debug32(self, address): - cmd = bytearray(16) - cmd[0:2] = DEBUG_COMMAND, JTAG_READDEBUG_32BIT - cmd[2:6] = struct.pack('<I', address) # pack into u32 little endian - reply = self.send_recv(cmd, 8) - return struct.unpack('<I', reply[4:8])[0] - def write_reg(self, reg, value): - cmd = bytearray(16) - cmd[0:3] = DEBUG_COMMAND, DEBUG_WRITEREG, reg - cmd[3:7] = struct.pack('<I', value) - r = self.send_recv(cmd, 2) - def read_reg(self, reg): - cmd = bytearray(16) - cmd[0:3] = DEBUG_COMMAND, DEBUG_READREG, reg - reply = self.send_recv(cmd, 4) - return struct.unpack('<I', reply)[0] - def read_all_regs(self): - cmd = bytearray(16) - cmd[0:2] = DEBUG_COMMAND, DEBUG_READALLREGS - reply = self.send_recv(cmd, 84) - fmt = '<' + 'I' * 21 # unpack 21 register values - return list(struct.unpack(fmt, reply)) - def write_mem32(self, address, content): - assert len(content) % 4 == 0 - cmd = bytearray(16) - cmd[0:2] = DEBUG_COMMAND, DEBUG_WRITEMEM_32BIT - cmd[2:8] = struct.pack('<IH', address, len(content)) - self.send_recv(cmd) - self.send_recv(content) - def read_mem32(self, address, length): - assert length % 4 == 0 - cmd = bytearray(16) - cmd[0:2] = DEBUG_COMMAND, DEBUG_READMEM_32BIT - cmd[2:8] = struct.pack('<IH', address, length) - reply = self.send_recv(cmd, length) # expect memory back! - return reply - - # Helper 2 functions: - def send_recv(self, tx, rxsize=0): - """ Helper function that transmits and receives data in bulk mode. """ - # TODO: we could use here the non-blocking libusb api. - tx = bytes(tx) - #assert len(tx) == 16 - self.devHandle.bulkWrite(2, tx) # write to endpoint 2 - if rxsize > 0: - return self.devHandle.bulkRead(1, rxsize) # read from endpoint 1 - def recv_ep3(self, rxsize): - return self.devHandle.bulkRead(3, rxsize) - -if __name__ == '__main__': - # Test program - sl = STLink2() - sl.open() - sl.reset() - print('version:', sl.Version) - print('mode before doing anything:', sl.CurrentModeString) - if sl.CurrentMode == DFU_MODE: - sl.exitDfuMode() - sl.enterSwdMode() - print('mode after entering swd mode:', sl.CurrentModeString) - - i = sl.ChipId - print('chip id: 0x{0:X}'.format(i)) - print('cpu: {0}'.format(sl.CpuId)) - - print('status: {0}'.format(sl.StatusString)) - - # test registers: - sl.write_reg(0, 0xdeadbeef) - sl.write_reg(1, 0xcafebabe) - sl.write_reg(2, 0xc0ffee) - sl.write_reg(3, 0x1337) - sl.write_reg(5, 0x1332) - sl.write_reg(6, 0x12345) - assert sl.read_reg(3) == 0x1337 - assert sl.read_reg(5) == 0x1332 - assert sl.read_reg(6) == 0x12345 - regs = sl.read_all_regs() - for i in range(len(regs)): - print('R{0}=0x{1:X}'.format(i, regs[i])) - - print('tracing') - sl.traceEnable() - sl.run() - sl.writePort0(0x1337) # For test - time.sleep(0.1) - td = sl.readTraceData() - print('trace data:', td) - - # Test CoreSight registers: - idr4 = sl.read_debug32(0xE0041fd0) - print('idr4 =', idr4) - - print('== ADI ==') - a = adi.Adi(sl) - a.parseRomTable(0xE00FF000) # why is rom table at 0xE00FF000? - print('== ADI ==') - - # Detect ROM table: - id4 = sl.read_debug32(0xE00FFFD0) - id5 = sl.read_debug32(0xE00FFFD4) - id6 = sl.read_debug32(0xE00FFFD8) - id7 = sl.read_debug32(0xE00FFFDC) - id0 = sl.read_debug32(0xE00FFFE0) - id1 = sl.read_debug32(0xE00FFFE4) - id2 = sl.read_debug32(0xE00FFFE8) - id3 = sl.read_debug32(0xE00FFFEC) - pIDs = [id0, id1, id2, id3, id4, id5, id6, id7] - print(pIDs) - - print('reading from 0xE00FF000') - scs = sl.read_debug32(0xE00FF000) - print('scs {0:08X}'.format(scs)) - dwt = sl.read_debug32(0xE00FF004) - print('dwt {0:08X}'.format(dwt)) - fpb = sl.read_debug32(0xE00FF008) - print('fpb {0:08X}'.format(fpb)) - itm = sl.read_debug32(0xE00FF00C) - print('itm {0:08X}'.format(itm)) - tpiu = sl.read_debug32(0xE00FF010) - print('tpiu {0:08X}'.format(tpiu)) - etm = sl.read_debug32(0xE00FF014) - print('etm {0:08X}'.format(etm)) - assert sl.read_debug32(0xE00FF018) == 0x0 # end marker - - devid = sl.read_debug32(0xE0040FC8) - print('TPIU_DEVID: {0:X}'.format(devid)) - devtype = sl.read_debug32(0xE0040FCC) - print('TPIU_TYPEID: {0:X}'.format(devtype)) - - sl.exitDebugMode() - print('mode at end:', sl.CurrentModeString) - - sl.close() - print('Test succes!') -
--- a/python/utils/stm32.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,264 +0,0 @@ -import time -import logging -from devices import Device, registerDevice, STLinkException, Interface -import stlink - -# F4 specifics: -STM32_FLASH_BASE = 0x08000000 -STM32_SRAM_BASE = 0x20000000 - -# flash registers: -FLASH_F4_REGS_ADDR = 0x40023c00 -FLASH_F4_KEYR = FLASH_F4_REGS_ADDR + 0x04 -FLASH_F4_SR = FLASH_F4_REGS_ADDR + 0x0c -FLASH_F4_CR = FLASH_F4_REGS_ADDR + 0x10 - -FLASH_F4_CR_START = 16 -FLASH_F4_CR_LOCK = 31 -FLASH_CR_PG = 0 -FLASH_F4_CR_SER = 1 -FLASH_CR_MER = 2 -FLASH_F4_CR_SNB = 3 -FLASH_F4_CR_SNB_MASK = 0x38 -FLASH_F4_SR_BSY = 16 - -class Stm32F4(Device): - """ - Implementation of the specifics of the STM32F4xx device series. - """ - def __init__(self, iface): - super().__init__(iface) - self.logger = logging.getLogger('stm32') - - def __str__(self): - return 'STM32F4 device size=0x{1:X} id=0x{0:X}'.format(\ - self.UID, self.FlashSize) - - def calculate_F4_sector(self, address): - sectorstarts = [] - a = STM32_FLASH_BASE - for sectorsize in self.sectorsizes: - sectorstarts.append(a) - a += sectorsize - # linear search: - sec = 0 - while sec < len(self.sectorsizes) and address >= sectorstarts[sec]: - sec += 1 - sec -= 1 # one back. - return sec, self.sectorsizes[sec] - - def calcSectors(self, address, size): - off = 0 - sectors = [] - while off < size: - sectornum, sectorsize = self.calculate_F4_sector(address + off) - sectors.append((sectornum, sectorsize)) - off += sectorsize - return sectors - - # Device registers: - @property - def UID(self): - uid_base = 0x1FFF7A10 - uid1 = self.iface.read_debug32(uid_base) - uid2 = self.iface.read_debug32(uid_base + 0x4) - uid3 = self.iface.read_debug32(uid_base + 0x8) - return (uid3 << 64) | (uid2 << 32) | uid1 - - @property - def FlashSize(self): - f_id = self.iface.read_debug32(0x1FFF7A22) - f_id = f_id >> 16 - return f_id * 1024 - - @property - def Running(self): - return self.iface.Status == stlink.CORE_RUNNING - - # flashing commands: - def writeFlash(self, address, content): - flashsize = self.FlashSize - pagesize = min(self.sectorsizes) - - # Check address range: - if address < STM32_FLASH_BASE: - raise STLinkException('Flashing below flash start') - if address + len(content) > STM32_FLASH_BASE + flashsize: - raise STLinkException('Flashing above flash size') - if address & 1 == 1: - raise STLinkException('Unaligned flash') - if len(content) & 1 == 1: - self.logger.warning('unaligned length, padding with zero') - content += bytes([0]) - if address & (pagesize - 1) != 0: - raise STLinkException('Address not aligned with pagesize') - # erase required space - sectors = self.calcSectors(address, len(content)) - self.logger.info('erasing {0} sectors'.format(len(sectors))) - for sector, secsize in sectors: - self.logger.info('erasing sector {0} of {1} bytes'.format(sector, secsize)) - self.eraseFlashSector(sector) - # program pages: - self.unlockFlashIf() - self.writeFlashCrPsiz(2) # writes are 32 bits aligned - self.setFlashCrPg() - self.logger.info('writing {0} bytes'.format(len(content))) - offset = 0 - t1 = time.time() - while offset < len(content): - size = len(content) - offset - if size > 0x8000: - size = 0x8000 - chunk = content[offset:offset + size] - while len(chunk) % 4 != 0: - chunk = chunk + bytes([0]) - # Use simple mem32 writes: - self.iface.write_mem32(address + offset, chunk) - offset += size - self.logger.info('{}%'.format(offset*100/len(content))) - self.logger.info('Done!') - self.lockFlash() - # verfify program: - self.verifyFlash(address, content) - - def eraseFlashSector(self, sector): - self.waitFlashBusy() - self.unlockFlashIf() - self.writeFlashCrSnb(sector) - self.setFlashCrStart() - self.waitFlashBusy() - self.lockFlash() - - def eraseFlash(self): - self.waitFlashBusy() - self.unlockFlashIf() - self.setFlashCrMer() - self.setFlashCrStart() - self.waitFlashBusy() - self.clearFlashCrMer() - self.lockFlash() - - def verifyFlash(self, address, content): - device_content = self.readFlash(address, len(content)) - ok = content == device_content - if ok: - self.logger.info('Verify: OK') - else: - self.logger.warning('Verify: Mismatch') - - def readFlash(self, address, size): - self.logger.info('Reading {1} bytes from 0x{0:X}'.format(address, size)) - offset = 0 - tmp_size = 0x1800 - image = bytes() - while offset < size: - # Correct for last page: - if offset + tmp_size > size: - tmp_size = size - offset - - # align size to 4 bytes: - aligned_size = tmp_size - while aligned_size % 4 != 0: - aligned_size += 1 - - mem = self.iface.read_mem32(address + offset, aligned_size) - image += mem[:tmp_size] - - # indicate progress: - self.logger.info('{}%'.format(100*len(image) / size)) - - # increase for next piece: - offset += tmp_size - assert size == len(image) - self.logger.info('Done!') - return image - - def waitFlashBusy(self): - """ block until flash operation completes. """ - while self.isFlashBusy(): - time.sleep(0.01) - - def isFlashLocked(self): - mask = 1 << FLASH_F4_CR_LOCK - return self.Cr & mask == mask - - def unlockFlashIf(self): - FLASH_KEY1, FLASH_KEY2 = 0x45670123, 0xcdef89ab - if self.isFlashLocked(): - self.iface.write_debug32(FLASH_F4_KEYR, FLASH_KEY1) - self.iface.write_debug32(FLASH_F4_KEYR, FLASH_KEY2) - if self.isFlashLocked(): - raise STLinkException('Failed to unlock') - - def lockFlash(self): - self.Cr = self.Cr | (1 << FLASH_F4_CR_LOCK) - - def readFlashSr(self): - return self.iface.read_debug32(FLASH_F4_SR) - - def readFlashCr(self): - return self.iface.read_debug32(FLASH_F4_CR) - - def writeFlashCr(self, x): - self.iface.write_debug32(FLASH_F4_CR, x) - - Cr = property(readFlashCr, writeFlashCr) - - def writeFlashCrSnb(self, sector): - x = self.Cr - x &= ~FLASH_F4_CR_SNB_MASK - x |= sector << FLASH_F4_CR_SNB - x |= 1 << FLASH_F4_CR_SER - self.Cr = x - - def setFlashCrMer(self): - self.Cr = self.Cr | (1 << FLASH_CR_MER) - - def setFlashCrPg(self): - self.Cr = self.Cr | (1 << FLASH_CR_PG) - - def writeFlashCrPsiz(self, n): - x = self.Cr - x &= (0x3 << 8) - x |= n << 8 - self.Cr = x - - def clearFlashCrMer(self): - x = self.Cr - x &= ~(1 << FLASH_CR_MER) - self.Cr = x - - def setFlashCrStart(self): - self.Cr = self.Cr | (1 << FLASH_F4_CR_START) - - def isFlashBusy(self): - mask = 1 << FLASH_F4_SR_BSY - sr = self.readFlashSr() - # Check for error bits: - errorbits = {} - errorbits[7] = 'Programming sequence error' - errorbits[6] = 'Programming parallelism error' - errorbits[5] = 'Programming alignment error' - errorbits[4] = 'Write protection error' - errorbits[1] = 'Operation error' - #errorbits[0] = 'End of operation' - for bit, msg in errorbits.items(): - if sr & (1 << bit) == (1 << bit): - raise STLinkException(msg) - return sr & mask == mask - - -@registerDevice(0x10016413) -class Stm32F40x(Stm32F4): - """ STM32F40x and STM32F41x device series """ - def __init__(self, iface): - super().__init__(iface) - # Assert the proper size for this device: - assert self.FlashSize == 0x100000 - """ - from 0x8000000 to 0x80FFFFF - 4 sectors of 0x4000 (16 kB) - 1 sector of 0x10000 (64 kB) - 7 of 0x20000 (128 kB) - """ - self.sectorsizes = [0x4000] * 4 + [0x10000] + [0x20000] * 7
--- a/python/utils/usb.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,245 +0,0 @@ -from ctypes import Structure, POINTER, CDLL, CFUNCTYPE -from ctypes import c_uint16, c_uint8, c_int, c_uint, c_ssize_t, c_void_p -from ctypes import byref, create_string_buffer - -# libusb wrapper: -libusb = CDLL('libusb-1.0.so') - -# helper: -def buildfunc(name, argtypes, restype=c_int): - f = getattr(libusb, name) - f.argtypes = argtypes - f.restype = restype - globals()[name] = f - return f -def enum(**enums): - reverse = dict((value, key) for key, value in enums.items()) - enums['reverse_mapping'] = reverse - return type('enum', (), enums) - -# enums -libusb_class_code = enum(PER_INTERFACE=0, AUDIO=1, COMM=2, HID=3, \ - PHYSICAL=5, PRINTER=7, PTP=6, MASS_STORAGE=8, HUB=9, \ - DATA=10, SMART_CARD=0xb, CONTENT_SECURITY=0xd, VIDEO=0xe, \ - PERSONAL_HEALTHCARE=0xf, DIAGNOSTIC_DEVICE=0xdc, WIRELESS=0xe,\ - APPLICATION=0xfe, VENDOR_SPEC=0xff) -libusb_speed = enum(UNKNOWN=0, LOW=1, FULL=2, HIGH=3, SUPER=4) -libusb_error = enum(SUCCES=0, ERROR_IO=-1, ERROR_INVALID_PARAM=-2, \ - ERROR_ACCESS=-3, ERROR_NO_DEVICE=-4, ERROR_NOT_FOUND=-5, \ - ERROR_BUSY=-6, ERROR_TIMEOUT=-7, ERROR_OVERFLOW=-8, \ - ERROR_PIPE=-9, ERROR_INTERRUPTED=-10, ERROR_NO_MEM=-11, \ - ERROR_NOT_SUPPORTED=-12, ERROR_OTHER=-99) -libusb_transfer_status = enum(\ - COMPLETED=0, ERROR=1, TIMED_OUT=2, \ - CANCELLED=3, STALL=4, NO_DEVICE=5, OVERFLOW=6) - -# types -c_int_p = POINTER(c_int) -class libusb_context(Structure): - pass -libusb_context_p = POINTER(libusb_context) -libusb_context_p_p = POINTER(libusb_context_p) - -class libusb_device(Structure): - pass -libusb_device_p = POINTER(libusb_device) -libusb_device_p_p = POINTER(libusb_device_p) -libusb_device_p_p_p = POINTER(libusb_device_p_p) - -class libusb_device_handle(Structure): - pass -libusb_device_handle_p = POINTER(libusb_device_handle) -libusb_device_handle_p_p = POINTER(libusb_device_handle_p) - -class libusb_device_descriptor(Structure): - _fields_ = [ - ('bLength', c_uint8), - ('bDescriptorType', c_uint8), - ('bcdUSB', c_uint16), - ('bDeviceClass', c_uint8), - ('bDeviceSubClass', c_uint8), - ('bDeviceProtocol', c_uint8), - ('bMaxPacketSize0', c_uint8), - ('idVendor', c_uint16), - ('idProduct', c_uint16), - ('bcdDevice', c_uint16), - ('iManufacturer', c_uint8), - ('iProduct', c_uint8), - ('iSerialNumber', c_uint8), - ('iNumConfigurations', c_uint8) - ] -libusb_device_descriptor_p = POINTER(libusb_device_descriptor) - -""" -class libusb_transfer(Structure): - pass -libusb_transfer_p = POINTER(libusb_transfer) -libusb_transfer_cb_fn = CFUNCTYPE(None, libusb_transfer_p) -libusb_transfer._fields_ = [ - ('dev_handle', libusb_device_handle_p), - ('flags', c_uint8), - ('endpoint', c_uchar), - ('type', c_uchar), - ('timeout', c_uint), - ('status', c_int), # enum libusb_transfer_status - ('length', c_int), - ('actual_length', c_int), - ('callback', libusb_transfer_cb_fn), - ('userdata', c_void_p), - ('buffer', c_void_p), - ('num_iso_packets', c_int), - ('iso_packet_desc', libusb_iso_packet_descriptor) - ] -""" -# functions -buildfunc('libusb_init', [libusb_context_p_p], c_int) - -buildfunc('libusb_get_device_list', \ - [libusb_context_p, libusb_device_p_p_p], c_ssize_t) -buildfunc('libusb_free_device_list', [libusb_device_p_p, c_int], None) -buildfunc('libusb_get_bus_number', [libusb_device_p], c_uint8) -buildfunc('libusb_get_device_address', [libusb_device_p], c_uint8) -buildfunc('libusb_get_device_speed', [libusb_device_p]) -buildfunc('libusb_unref_device', [libusb_device_p], None) -buildfunc('libusb_open', [libusb_device_p, libusb_device_handle_p_p]) -buildfunc('libusb_close', [libusb_device_handle_p], None) -buildfunc('libusb_get_configuration',[libusb_device_handle_p,POINTER(c_int)]) -buildfunc('libusb_set_configuration', [libusb_device_handle_p, c_int]) -buildfunc('libusb_claim_interface', [libusb_device_handle_p, c_int]) - -buildfunc('libusb_get_device_descriptor',\ - [libusb_device_p, libusb_device_descriptor_p]) - -# synchronous functions: -buildfunc('libusb_bulk_transfer', [libusb_device_handle_p, c_uint8, \ - c_void_p, c_int, c_int_p, c_uint]) - -# pythonic API: - -class UsbError(Exception): - def __init__(self, msg, errorcode): - if errorcode in libusb_error.reverse_mapping: - errorcode = libusb_error.reverse_mapping[errorcode] - msg = msg + 'Error code: {0}'.format(errorcode) - super().__init__(msg) - -class UsbContext(object): - """ A usb context in case of multiple use """ - def __init__(self): - self.context_p = libusb_context_p() - r = libusb_init(byref(self.context_p)) - if r != 0: - raise UsbError('libusb_init error!', r) - def getDeviceList(self): - devlist = libusb_device_p_p() - count = libusb_get_device_list(self.context_p, byref(devlist)) - if count < 0: - raise UsbError('Error getting device list', count) - l = [UsbDevice(self, device_p.contents) for device_p in devlist[0:count]] - libusb_free_device_list(devlist, 0) - return l - DeviceList = property(getDeviceList) - -class UsbDevice: - """ A detected usb device """ - def __init__(self, context, device_p): - self.context = context - self.dev_p = device_p - def __del__(self): - libusb_unref_device(self.dev_p) - def getBusNumber(self): - return libusb_get_bus_number(self.dev_p) - BusNumber = property(getBusNumber) - def getDeviceAddress(self): - return libusb_get_device_address(self.dev_p) - DeviceAddress = property(getDeviceAddress) - def getSpeed(self): - s = libusb_get_device_speed(self.dev_p) - if s in libusb_speed.reverse_mapping: - s = libusb_speed.reverse_mapping[s] - return s - Speed = property(getSpeed) - def getDescriptor(self): - descriptor = libusb_device_descriptor() - r = libusb_get_device_descriptor(self.dev_p, byref(descriptor)) - if r != 0: - raise UsbError('Error getting descriptor', r) - return descriptor - Descriptor = property(getDescriptor) - VendorId = property(lambda self: self.Descriptor.idVendor) - ProductId = property(lambda self: self.Descriptor.idProduct) - NumConfigurations = property(lambda self: self.Descriptor.bNumConfigurations) - def open(self): - """ Opens this device and returns a handle """ - handle_p = libusb_device_handle_p() - r = libusb_open(self.dev_p, byref(handle_p)) - if r != 0: - raise UsbError('error opening device', r) - return UsbDeviceHandle(self, handle_p) - def __repr__(self): - r2 = 'Usb device: bus {0} address {1} {2:04X}:{3:04X} speed {4}' \ - .format( \ - self.BusNumber, self.DeviceAddress, self.VendorId, \ - self.ProductId, self.Speed) - return r2 - -USB_ENDPOINT_DIR_MASK = 0x80 -USB_ENDPOINT_IN = 0x80 -USB_ENDPOINT_OUT = 0x0 - -class UsbDeviceHandle: - """ Handle to a detected usb device """ - def __init__(self, device, handle_p): - self.device = device - self.handle_p = handle_p - def __del__(self): - self.close() - def close(self): - if self.handle_p: - libusb_close(self.handle_p) - self.handle_p = None - def getConfiguration(self): - config = c_int() - r = libusb_get_configuration(self.handle_p, byref(config)) - if r != 0: raise UsbError('Error getting configuration', r) - return config.value - def setConfiguration(self, config): - r = libusb_set_configuration(self.handle_p, config) - if r != 0: raise UsbError('Error setting configuration', r) - Configuration = property(getConfiguration, setConfiguration) - def claimInterface(self, interface_number): - r = libusb_claim_interface(self.handle_p, interface_number) - if r != 0: raise UsbError('Error claiming interface', r) - def bulkWrite(self, endpoint, data, timeout=0): - """ Synchronous bulk write """ - assert type(data) is bytes - # assure the endpoint indicates the correct: - endpoint = (endpoint & (~USB_ENDPOINT_DIR_MASK)) | USB_ENDPOINT_OUT - buf = create_string_buffer(data) - transferred = c_int() - r = libusb_bulk_transfer(self.handle_p, endpoint, buf, len(data), \ - byref(transferred), timeout) - if r != 0: - raise UsbError('Bulk write failed', r) - if transferred.value != len(data): - raise UsbError('Not all {0} transferred {1}'.format(len(data), \ - transferred.value)) - def bulkRead(self, endpoint, numbytes, timeout=0): - """ Synchronous bulk read """ - # assure the endpoint indicates the correct: - endpoint = (endpoint & (~USB_ENDPOINT_DIR_MASK)) | USB_ENDPOINT_IN - buf = create_string_buffer(numbytes) - transferred = c_int() - r = libusb_bulk_transfer(self.handle_p, endpoint, buf, numbytes, \ - byref(transferred), timeout) - if r != 0: - raise UsbError('Bulk read failed', r) - if transferred.value != numbytes: - raise UsbError('Not all {0} transferred {1}'.format(numbytes, \ - transferred.value)) - data = buf.raw[0:numbytes] - return data - -class UsbTransfer: - def __init__(self): - libusb_alloc_transfer(0)
--- a/python/yacc.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,307 +0,0 @@ -#!/usr/bin/python - -""" -Parser generator utility. This script can generate a python script from a -grammar description. - -Invoke the script on a grammar specification file: - -.. code:: - - $ ./yacc.py test.x -o test_parser.py - -And use the generated parser by deriving a user class: - - -.. code:: - - import test_parser - class MyParser(test_parser.Parser): - pass - p = MyParser() - p.parse() - - -Alternatively you can load the parser on the fly: - -.. code:: - - import yacc - parser_mod = yacc.load_as_module('mygrammar.x') - class MyParser(parser_mod.Parser): - pass - p = MyParser() - p.parse() - -""" - -import argparse -import re -import sys -import datetime -import types -import io -import logging -from pyyacc import Grammar - - -class XaccLexer: - def __init__(self): - pass - - def feed(self, txt): - # Create a regular expression for the lexing part: - tok_spec = [ - ('ID', r'[A-Za-z][A-Za-z\d_]*'), - ('STRING', r"'[^']*'"), - ('BRACEDCODE', r"\{[^\}]*\}"), - ('OTHER', r'[:;\|]'), - ('SKIP', r'[ ]') - ] - tok_re = '|'.join('(?P<%s>%s)' % pair for pair in tok_spec) - gettok = re.compile(tok_re).match - - lines = txt.split('\n') - - def tokenize_line(line): - """ Generator that splits up a line into tokens """ - mo = gettok(line) - pos = 0 - while mo: - typ = mo.lastgroup - val = mo.group(typ) - if typ == 'ID': - yield (typ, val) - elif typ == 'STRING': - typ = 'ID' - yield (typ, val[1:-1]) - elif typ == 'OTHER': - typ = val - yield (typ, val) - elif typ == 'BRACEDCODE': - yield (typ, val) - elif typ == 'SKIP': - pass - else: - raise NotImplementedError(str(typ)) - pos = mo.end() - mo = gettok(line, pos) - if len(line) != pos: - raise ParseError('Lex fault at {}'.format(line)) - - def tokenize(): - section = 0 - for line in lines: - line = line.strip() - if not line: - continue # Skip empty lines - if line == '%%': - section += 1 - yield('%%', '%%') - continue - if section == 0: - if line.startswith('%tokens'): - yield('%tokens', '%tokens') - yield from tokenize_line(line[7:]) - else: - yield ('HEADER', line) - elif section == 1: - yield from tokenize_line(line) - yield ('eof', 'eof') - self.tokens = tokenize() - self.token = self.tokens.__next__() - - def next_token(self): - t = self.token - if t[0] != 'eof': - self.token = self.tokens.__next__() - return t - - -class ParseError(Exception): - pass - - -class XaccParser: - """ Implements a recursive descent parser to parse grammar rules. - We could have made an generated parser, but that would yield a chicken - egg issue. - """ - def __init__(self, lexer): - self.lexer = lexer - - @property - def Peak(self): - """ Sneak peak to the next token in line """ - return self.lexer.token[0] - - def next_token(self): - """ Take the next token """ - return self.lexer.next_token() - - def consume(self, typ): - """ Eat next token of type typ or raise an exception """ - if self.Peak == typ: - return self.next_token() - else: - raise ParseError('Expected {}, but got {}'.format(typ, self.Peak)) - - def has_consumed(self, typ): - """ Consume typ if possible and return true if so """ - if self.Peak == typ: - self.consume(typ) - return True - return False - - def parse_grammar(self): - """ Entry parse function into recursive descent parser """ - # parse header - headers = [] - terminals = [] - while self.Peak in ['HEADER', '%tokens']: - if self.Peak == '%tokens': - self.consume('%tokens') - while self.Peak == 'ID': - terminals.append(self.consume('ID')[1]) - else: - headers.append(self.consume('HEADER')[1]) - self.consume('%%') - self.headers = headers - self.grammar = Grammar(terminals) - while self.Peak != 'eof': - self.parse_rule() - return self.grammar - - def parse_symbol(self): - return self.consume('ID')[1] - - def parse_rhs(self): - """ Parse the right hand side of a rule definition """ - symbols = [] - while self.Peak not in [';', 'BRACEDCODE', '|']: - symbols.append(self.parse_symbol()) - if self.Peak == 'BRACEDCODE': - action = self.consume('BRACEDCODE')[1] - action = action[1:-1].strip() - else: - action = None - return symbols, action - - def parse_rule(self): - """ Parse a rule definition """ - p = self.parse_symbol() - self.consume(':') - symbols, action = self.parse_rhs() - self.grammar.add_production(p, symbols, action) - while self.has_consumed('|'): - symbols, action = self.parse_rhs() - self.grammar.add_production(p, symbols, action) - self.consume(';') - - -class XaccGenerator: - """ Generator that writes generated parser to file """ - def __init__(self): - self.logger = logging.getLogger('yacc') - - def generate(self, grammar, headers, output_file): - self.output_file = output_file - self.grammar = grammar - self.headers = headers - self.logger.info('Generating parser for grammar {}'.format(grammar)) - self.action_table, self.goto_table = grammar.generate_tables() - self.generate_python_script() - - def print(self, *args): - """ Print helper function that prints to output file """ - print(*args, file=self.output_file) - - def generate_python_script(self): - """ Generate python script with the parser table """ - self.print('#!/usr/bin/python') - stamp = datetime.datetime.now().ctime() - self.print('""" Automatically generated by xacc on {} """'.format(stamp)) - self.print('from pyyacc import LRParser, Reduce, Shift, Accept, Production, Grammar') - self.print('from ppci import Token') - self.print('') - for h in self.headers: - print(h, file=output_file) - self.print('') - self.print('class Parser(LRParser):') - self.print(' def __init__(self):') - # Generate rules: - self.print(' self.start_symbol = "{}"'.format(self.grammar.start_symbol)) - self.print(' self.grammar = Grammar({})'.format(self.grammar.terminals)) - for rule_number, rule in enumerate(self.grammar.productions): - rule.f_name = 'action_{}_{}'.format(rule.name, rule_number) - self.print(' self.grammar.add_production("{}", {}, self.{})'.format(rule.name, rule.symbols, rule.f_name)) - # Fill action table: - self.print(' self.action_table = {}') - for state in self.action_table: - action = self.action_table[state] - self.print(' self.action_table[{}] = {}'.format(state, action)) - self.print('') - - # Fill goto table: - self.print(' self.goto_table = {}') - for state_number in self.goto_table: - to = self.goto_table[state_number] - self.print(' self.goto_table[{}] = {}'.format(state_number, to)) - self.print('') - - # Generate a function for each action: - for rule in self.grammar.productions: - num_symbols = len(rule.symbols) - args = ', '.join('arg{}'.format(n + 1) for n in range(num_symbols)) - self.print(' def {}(self, {}):'.format(rule.f_name, args)) - if rule.f == None: - semantics = 'pass' - else: - semantics = str(rule.f) - if semantics.strip() == '': - semantics = 'pass' - for n in range(num_symbols): - semantics = semantics.replace('${}'.format(n + 1), 'arg{}'.format(n + 1)) - self.print(' {}'.format(semantics)) - self.print('') - - -def make_argument_parser(): - # Parse arguments: - parser = argparse.ArgumentParser(description='xacc compiler compiler') - parser.add_argument('source', type=argparse.FileType('r'), \ - help='the parser specification') - parser.add_argument('-o', '--output', type=argparse.FileType('w'), \ - default=sys.stdout) - return parser - - -def load_as_module(filename): - """ Load a parser spec file, generate LR tables and create module """ - ob = io.StringIO() - args = argparse.Namespace(source=open(filename), output=ob) - main(args) - - parser_mod = types.ModuleType('generated_parser') - exec(ob.getvalue(), parser_mod.__dict__) - return parser_mod - - -def main(args): - src = args.source.read() - args.source.close() - - # Construction of generator parts: - lexer = XaccLexer() - parser = XaccParser(lexer) - generator = XaccGenerator() - - # Sequence source through the generator parts: - lexer.feed(src) - grammar = parser.parse_grammar() - generator.generate(grammar, parser.headers, args.output) - - -if __name__ == '__main__': - args = make_argument_parser().parse_args() - main(args)
--- a/python/zcc.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -#!/usr/bin/env python - -import sys -import os -import argparse -import logging - -from ppci.buildtasks import Compile, Assemble, Link -from ppci.tasks import TaskRunner -from ppci.report import RstFormatter -from ppci.objectfile import ObjectFile -from ppci.target.target_list import target_list -from ppci.recipe import RecipeLoader -import ppci - - -def logLevel(s): - """ Converts a string to a valid logging level """ - numeric_level = getattr(logging, s.upper(), None) - if not isinstance(numeric_level, int): - raise ValueError('Invalid log level: {}'.format(s)) - return numeric_level - - -targets = {t.name: t for t in target_list} -targetnames = list(targets.keys()) - -def make_parser(): - parser = argparse.ArgumentParser(description='lcfos Compiler') - - parser.add_argument('--log', help='Log level (INFO,DEBUG,[WARN])', - type=logLevel, default='INFO') - parser.add_argument('--display-build-steps', action='store_true') - sub_parsers = parser.add_subparsers(title='commands', - description='possible commands', dest='command') - recipe_parser = sub_parsers.add_parser('recipe', help="Bake recipe") - recipe_parser.add_argument('recipe_file', help='recipe file') - - compile_parser = sub_parsers.add_parser('compile', help="compile source") - compile_parser.add_argument('source', type=argparse.FileType('r'), - help='the source file to build', nargs="+") - compile_parser.add_argument('-i', '--imp', type=argparse.FileType('r'), - help='Possible import module', action='append', default=[]) - compile_parser.add_argument('--target', help="Backend selection", - choices=targetnames, required=True) - compile_parser.add_argument('-o', '--output', help='Output file', - metavar='filename') - compile_parser.add_argument('--report', - help='Specify a file to write the compile report to', - type=argparse.FileType('w')) - return parser - - - -def main(args): - # Configure some logging: - logging.getLogger().setLevel(logging.DEBUG) - ch = logging.StreamHandler() - ch.setFormatter(logging.Formatter(ppci.logformat)) - ch.setLevel(args.log) - logging.getLogger().addHandler(ch) - - runner = TaskRunner() - if args.command == 'compile': - tg = targets[args.target] - output = ObjectFile() - runner.add_task(Compile(args.source, args.imp, tg, output)) - elif args.command == 'recipe': - recipe_loader = RecipeLoader() - recipe_loader.load_file(args.recipe_file, runner) - else: - raise NotImplementedError('Invalid option') - - if args.display_build_steps: - runner.display() - res = 0 - else: - res = runner.run_tasks() - - logging.getLogger().removeHandler(ch) - return res - - -if __name__ == '__main__': - parser = make_parser() - arguments = parser.parse_args() - if not arguments.command: - parser.print_usage() - sys.exit(1) - sys.exit(main(arguments))
--- a/readme.rst Fri Mar 07 17:10:21 2014 +0100 +++ b/readme.rst Thu Feb 19 14:10:52 2015 +0100 @@ -15,54 +15,61 @@ 'kernel' contains the microkernel. 'python' contains the python utilities. -Software dependencies ---------------------- + +How to run this? +---------------- + +Install required software: * python3.3 -* pyyaml for yaml module * (optional) pyqt5, pyqt4 or pyside -How to start the IDE --------------------- +Checkout the code: .. code:: bash - cd python - python ide.py + hg clone https://bitbucket.org/windel/lcfos + cd lcfos + +Run some unit tests: + +.. code:: bash + + cd test + python3 run_tests.py -Source code ------------ +Weblinks +-------- + +Docs are located here: +http://lcfos.readthedocs.org/en/latest/ Sources are located here: https://bitbucket.org/windel/lcfos - -Docs ----- - -Docs are located here: -http://lcfos.readthedocs.org/en/latest/ +here: +http://hg.assembla.com/lcfOS/ -Run unittests -------------- - -.. code:: bash +and here: +https://pikacode.com/windel/lcfos/ - cd test - ./runtests.sh - - -Status ------- The project is contains tests which are run continuously at drone.io. .. image:: https://drone.io/bitbucket.org/windel/lcfos/status.png +https://drone.io/bitbucket.org/windel/lcfos + +Repository metrics: + .. image:: https://www.ohloh.net/p/lcfos/widgets/project_thin_badge.gif +http://www.ohloh.net/p/lcfos + -https://drone.io/bitbucket.org/windel/lcfos +Live demo is at redhat openshift: + +http://lcfos-windel.rhcloud.com/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/run.sh Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +# -S means halt at start: +qemu-system-arm -M vexpress-a9 -m 128M -kernel kernel_arm.bin \ + -serial stdio +
--- a/test/grind.py Fri Mar 07 17:10:21 2014 +0100 +++ b/test/grind.py Thu Feb 19 14:10:52 2015 +0100 @@ -17,5 +17,6 @@ p = cProfile.Profile() s = p.run('runtests()') stats = pstats.Stats(p) - stats.sort_stats('tottime') + #stats.sort_stats('tottime') + stats.sort_stats('cumtime') stats.print_stats(.1)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/m3_bare/m3bare.mmap Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,10 @@ + +MEMORY flash LOCATION=0x0 SIZE=0x10000 { + SECTION(code) +} + +MEMORY ram LOCATION=0x20000000 SIZE=0x10000 { + SECTION(data) +} + +
--- a/test/m3_bare/recipe.yaml Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ - -link: - inputs: - - assemble: - source: startup_m3.asm - machine: thumb - - compile: - sources: [hello.c3] - includes: [] - machine: thumb - layout: - code: 0x0 - data: 0x20000000 - output: bare.bin -
--- a/test/m3_bare/startup_m3.asm Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ - - -DCD 0x20000678 ; Setup stack pointer -DCD 0x00000009 ; Reset vector, jump to address 8 -B hello_main ; Branch to main (this is actually in the interrupt vector) -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/run_tests.py Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,17 @@ +import sys +import unittest + +if sys.version_info.major < 3: + print('Requires python 3 at least') + sys.exit() + +sys.path.insert(0, '../python') + +loader = unittest.TestLoader() +suite = unittest.TestSuite() + +for test in loader.discover('.'): + suite.addTest(test) + +unittest.TextTestRunner().run(suite) +
--- a/test/runtests.sh Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -export PYTHONPATH=$PYTHONPATH:`pwd`/../python:`pwd`/../python/ide - -if [ "$1" == "loop" ] -then - DIR=.. - while :; do - python -m unittest - #python -m unittest -v - echo "Awaiting changes in $DIR" - inotifywait -r -e modify $DIR - done -else - set -e - python -m unittest -fi - -
--- a/test/sample4.brg Fri Mar 07 17:10:21 2014 +0100 +++ b/test/sample4.brg Thu Feb 19 14:10:52 2015 +0100 @@ -6,17 +6,17 @@ %% -stmt: ASGNI(disp, reg) 1 (. self.tr(1) .) -stmt: reg 0 (. self.tr(2).) -reg: ADDI(reg, rc) 1 (. self.tr(3) .) -reg: CVCI(INDIRC(disp)) 1 (. self.tr(4) .) -reg: IOI 0 (. self.tr(5).) -reg: disp 1 (. self.tr(6).) -disp: ADDI(reg, con) 1 (. self.tr(7).) -disp: ADDRLP 0 (. self.tr(8).) -rc: con 0 (. self.tr(9).) -rc: reg 0 (. self.tr(10).) -con: CNSTI 0 (. self.tr(11).) -con: IOI 0 (. self.tr(12).) +stmt: ASGNI(disp, reg) 1 'self.tr(1) ' +stmt: reg 0 ' self.tr(2)' +reg: ADDI(reg, rc) 1 'self.tr(3) ' +reg: CVCI(INDIRC(disp)) 1 'self.tr(4) ' +reg: IOI 0 'self.tr(5)' +reg: disp 1 'self.tr(6)' +disp: ADDI(reg, con) 1 'self.tr(7)' +disp: ADDRLP 0 ' self.tr(8)' +rc: con 0 ' self.tr(9)' +rc: reg 0 'self.tr(10)' +con: CNSTI 0 ' self.tr(11)' +con: IOI 0 ' self.tr(12)'
--- a/test/setenv.sh Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -export PYTHONPATH=$PYTHONPATH:`pwd`/../python - -export TESTEMU=1 -
--- a/test/test_burm.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -import unittest -import io -import argparse - -from tree import Tree -import pyburg - - -class testBURG(unittest.TestCase): - def testSample4(self): - """ Test sample4 burg system """ - # Generate matcher from spec: - buf = io.StringIO() - args = argparse.Namespace(source=open('sample4.brg'), output=buf) - pyburg.main(args) - - # Execute generated script into global scope: - exec(buf.getvalue(), globals()) - - # Sample tree: - t = Tree('ASGNI', - Tree('ADDRLP'), - Tree('ADDI', - Tree('CVCI', Tree('INDIRC', Tree('ADDRLP'))), - Tree('CNSTI') - ) - ) - - # Subclass generated matcher: - class MyMatcher(Matcher): - def __init__(self): - super().__init__() - self.trace = [] - - def tr(self, r): - self.trace.append(r) - - # Match tree: - mm = MyMatcher() - mm.gen(t) - self.assertSequenceEqual([8,8,4,11,9,3,1], mm.trace) - -if __name__ == '__main__': - unittest.main()
--- a/test/testarmasm.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ -import unittest -from ppci.outstream import BinaryOutputStream -from ppci.objectfile import ObjectFile -from asm import Assembler -from testasm import AsmTestCaseBase -from ppci.target.target_list import arm_target - - -a = Assembler(arm_target) - -class ArmAssemblerTestCase(AsmTestCaseBase): - """ ARM-mode (not thumb-mode) instruction assembly test case """ - def setUp(self): - self.t = arm_target - self.obj = ObjectFile() - self.ostream = BinaryOutputStream(self.obj) - self.ostream.selectSection('.text') - self.a = a #Assembler(target=self.t) - - def testMovImm(self): - self.feed('mov r4, 100') - self.check('6440a0e3') - - def testMovImm2(self): - self.feed('mov sp, 0x6000') - self.check('06daa0e3') - - def testAdd2(self): - self.feed('add r12, r11, 300') - self.check('4bcf8be2') - - def testAdd1(self): - self.feed('add r9, r7, r2') - self.check('029087e0') - - def testSub1(self): - self.feed('sub r5, r6, r2') - self.check('025046e0') - - def testSub2(self): - self.feed('sub r0, r1, 0x80000001') - self.check('060141e2') - - def testOrr1(self): - self.feed('orr r8, r7, r6') - self.check('068087e1') - - def testBranches(self): - self.feed('b sjakie') - self.feed('ble sjakie') - self.feed('bgt sjakie') - self.feed('beq sjakie') - self.feed('bl sjakie') - self.feed('sjakie:') - self.feed('b sjakie') - self.feed('ble sjakie') - self.feed('bgt sjakie') - self.feed('beq sjakie') - self.feed('bl sjakie') - self.check('030000ea 020000da 010000ca 0000000a ffffffeb feffffea fdffffda fcffffca fbffff0a faffffeb') - - def testPush(self): - self.feed('push {r11,r5,r4,lr}') - self.check('30482de9') - - def testPop(self): - self.feed('pop {r4,r5,r6}') - self.check('7000bde8') - - def testStr(self): - self.feed('str r9, [r2, 33]') - self.check('219082e5') - - def testLdr(self): - self.feed('ldr r5, [r3, 87]') - self.check('575093e5') - - def testSequence1(self): - self.feed('sub r4,r5,23') - self.feed('blt x') - self.feed('x:') - self.feed('mul r4,r5,r2') - self.check('174045e2 ffffffba 950204e0') - - -if __name__ == '__main__': - unittest.main()
--- a/test/testasm.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -#!/usr/bin/python - -import unittest -from ppci import CompilerError -from ppci.assembler import tokenize, Assembler, Lexer -from ppci.objectfile import ObjectFile -from ppci.linker import Linker -from ppci.outstream import BinaryOutputStream -from ppci.target.basetarget import Label - - -class AssemblerLexingCase(unittest.TestCase): - """ Tests the assemblers lexer """ - - def testLex0(self): - """ Check if the lexer is OK """ - asmline, toks = 'mov rax, rbx ', ['ID', 'ID', ',', 'ID', 'EOF'] - self.assertSequenceEqual([tok.typ for tok in tokenize(asmline, [])], toks) - - def testLex1(self): - """ Test if lexer correctly maps some tokens """ - asmline, toks = 'lab1: mov rax, rbx ', ['ID', ':', 'ID', 'ID', ',', 'ID', 'EOF'] - self.assertSequenceEqual([tok.typ for tok in tokenize(asmline, [])], toks) - - def testLex2(self): - """ Test if lexer correctly maps some tokens """ - asmline, toks = 'mov 3.13 0xC 13', ['ID', 'REAL', 'val5', 'val5', 'EOF'] - self.assertSequenceEqual([tok.typ for tok in tokenize(asmline, [])], toks) - - def testLex3(self): - """ Test if lexer fails on a token that is invalid """ - asmline = '0z4: mov rax, rbx $ ' - with self.assertRaises(CompilerError): - list(tokenize(asmline, [])) - - -class AssemblerParsingTestCase(unittest.TestCase): - """ - Tests the assembler parts - """ - def setUp(self): - self.skipTest('refactoring asm parser') - self.parser = asmParser - self.stack = [] - - def emit(self, x): - self.stack.append(x) - - def parse_line(self, line): - self.parser.parse(Lexer(line), self.emit) - - def testParse(self): - asmline = 'lab1: mov rax, rbx' - self.parse_line(asmline) - - def expectTree(self, asmline, stack): - self.parse_line(asmline) - self.assertSequenceEqual(stack, self.stack) - - def testParse2(self): - asmline = 'a: mov rax, [rbx + 2]' - output = [] - output.append(ALabel('a')) - output.append(AInstruction('mov', [ASymbol('rax'), AUnop('[]', ASymbol('rbx') + ANumber(2))])) - self.expectTree(asmline, output) - - def testParse3(self): - # A label must be optional: - asmline = 'mov rax, 1' - output = [AInstruction('mov', [ASymbol('rax'), ANumber(1)])] - self.expectTree(asmline, output) - - def testParse4(self): - # Test 3 operands: - asmline = 'add rax, [4*rbx + 22], rcx' - ops = [] - ops.append(ASymbol('rax')) - ops.append(AUnop('[]', ANumber(4) * ASymbol('rbx') + ANumber(22))) - ops.append(ASymbol('rcx')) - output = [AInstruction('add', ops)] - self.expectTree(asmline, output) - - def testParse5(self): - # An instruction must be optional: - asmline = 'lab1:' - output = [] - output.append(ALabel('lab1')) - self.expectTree(asmline, output) - - def testParse6(self): - # A line can be empty - self.parse_line('') - - -class OustreamTestCase(unittest.TestCase): - def test1(self): - obj = ObjectFile() - o = BinaryOutputStream(obj) - o.selectSection('.text') - o.emit(Label('a')) - self.assertSequenceEqual(bytes(), obj.get_section('.text').data) - - -class AsmTestCaseBase(unittest.TestCase): - """ Base testcase for assembly """ - def feed(self, line): - self.a.assemble(line, self.ostream) - - def check(self, hexstr): - l = Linker() - self.obj = l.link([self.obj]) - data = bytes(self.obj.get_section('.text').data) - self.assertSequenceEqual(bytes.fromhex(hexstr), data) - - -if __name__ == '__main__': - unittest.main()
--- a/test/testbintools.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -import unittest -import sys -from ppci.target.arm.token import ArmToken -from ppci.linker import Linker -from ppci.objectfile import ObjectFile -from ppci import CompilerError -from ppci.tasks import EmptyTask, TaskRunner, TaskError - - -class TaskTestCase(unittest.TestCase): - def testCircular(self): - t1 = EmptyTask('t1') - t2 = EmptyTask('t2') - t1.add_dependency(t2) - with self.assertRaises(TaskError): - t2.add_dependency(t1) - - def testCircularDeeper(self): - t1 = EmptyTask('t1') - t2 = EmptyTask('t2') - t3 = EmptyTask('t3') - t1.add_dependency(t2) - t2.add_dependency(t3) - with self.assertRaises(TaskError): - t3.add_dependency(t1) - - def testSort(self): - t1 = EmptyTask('t1') - t2 = EmptyTask('t2') - runner = TaskRunner() - t1.add_dependency(t2) - runner.add_task(t1) - runner.add_task(t2) - runner.run_tasks() - - -class TokenTestCase(unittest.TestCase): - def testSetBits(self): - at = ArmToken() - at[2:4] = 0b11 - self.assertEqual(0xc, at.bit_value) - - def testSetBits(self): - at = ArmToken() - at[4:8] = 0b1100 - self.assertEqual(0xc0, at.bit_value) - - -class LinkerTestCase(unittest.TestCase): - def testUndefinedReference(self): - l = Linker() - o1 = ObjectFile() - o1.get_section('.text') - o1.add_relocation('undefined_sym', 0, 'rel8', '.text') - o2 = ObjectFile() - with self.assertRaises(CompilerError): - o3 = l.link([o1, o2]) - - def testDuplicateSymbol(self): - l = Linker() - o1 = ObjectFile() - o1.get_section('.text') - o1.add_symbol('a', 0, '.text') - o2 = ObjectFile() - o2.get_section('.text') - o2.add_symbol('a', 0, '.text') - with self.assertRaises(CompilerError): - o3 = l.link([o1, o2]) - - def testRel8Relocation(self): - l = Linker() - o1 = ObjectFile() - o1.get_section('.text').add_data(bytes([0]*100)) - o1.add_relocation('a', 0, 'rel8', '.text') - o2 = ObjectFile() - o2.get_section('.text').add_data(bytes([0]*100)) - o2.add_symbol('a', 24, '.text') - o3 = l.link([o1, o2]) - - def testSymbolValues(self): - l = Linker() - o1 = ObjectFile() - o1.get_section('.text').add_data(bytes([0]*108)) - o1.add_symbol('b', 24, '.text') - o2 = ObjectFile() - o2.get_section('.text').add_data(bytes([0]*100)) - o2.add_symbol('a', 2, '.text') - o3 = l.link([o1, o2]) - self.assertEqual(110, o3.find_symbol('a').value) - self.assertEqual(24, o3.find_symbol('b').value) - self.assertEqual(208, o3.get_section('.text').Size) - - def testMemoryLayout(self): - l = Linker() - memory_layout = {'.text': 0x08000000, '.data':0x20000000} - o1 = ObjectFile() - o1.get_section('.text').add_data(bytes([0]*108)) - o1.add_symbol('b', 24, '.text') - o2 = ObjectFile() - o2.get_section('.text').add_data(bytes([0]*100)) - o2.get_section('.data').add_data(bytes([0]*100)) - o2.add_symbol('a', 2, '.data') - o2.add_symbol('c', 2, '.text') - o3 = l.link([o1, o2], layout=memory_layout) - self.assertEqual(0x20000000+2, o3.find_symbol('a').value) - self.assertEqual(0x08000000+24, o3.find_symbol('b').value) - self.assertEqual(0x08000000+110, o3.find_symbol('c').value) - self.assertEqual(208, o3.get_section('.text').Size) - self.assertEqual(100, o3.get_section('.data').Size) - - -if __name__ == '__main__': - unittest.main() - sys.exit()
--- a/test/testbitfun.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ - - -import unittest -import sys -from ppci.bitfun import rotate_left, rotate_right - - -class BitRotationTestCase(unittest.TestCase): - def testRightRotation(self): - self.assertEqual(0xFF000000, rotate_right(0xFF, 8)) - self.assertEqual(0x0FF00000, rotate_right(0xFF, 12)) - - def testLeftRotation(self): - self.assertEqual(0x0000FF00, rotate_left(0xFF, 8)) - self.assertEqual(0x001FE000, rotate_left(0xFF, 13)) - - -if __name__ == '__main__': - unittest.main() - sys.exit()
--- a/test/testc3.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,476 +0,0 @@ -import unittest -import io -from ppci.c3 import Builder, Lexer -from ppci.target import SimpleTarget -import ppci - - -class testLexer(unittest.TestCase): - def setUp(self): - diag = ppci.DiagnosticsManager() - self.l = Lexer(diag) - - def testUnexpectedCharacter(self): - snippet = io.StringIO(""" var s \u6c34 """) - with self.assertRaises(ppci.CompilerError): - list(self.l.tokenize(snippet)) - - def check(self, snippet, toks): - toks2 = list(tok.typ for tok in self.l.tokenize(io.StringIO(snippet))) - self.assertSequenceEqual(toks, toks2) - - def testBlockComment(self): - snippet = """ - /* Demo */ - var int x = 0; - """ - toks = ['var', 'ID', 'ID', '=', 'NUMBER', ';', 'END'] - self.check(snippet, toks) - - def testBlockCommentMultiLine(self): - snippet = """ - /* Demo - bla1 - bla2 - */ - var int x = 0; - """ - toks = ['var', 'ID', 'ID', '=', 'NUMBER', ';', 'END'] - self.check(snippet, toks) - - -class testBuilder(unittest.TestCase): - def setUp(self): - self.diag = ppci.DiagnosticsManager() - self.builder = Builder(self.diag, SimpleTarget()) - self.diag.clear() - - def makeFileList(self, snippet): - """ Try to make a list with opened files """ - if type(snippet) is list: - l2 = [] - for s in snippet: - if type(s) is str: - l2.append(io.StringIO(s)) - else: - l2.append(s) - return l2 - else: - return [io.StringIO(snippet)] - - def expectErrors(self, snippet, rows): - """ Helper to test for expected errors on rows """ - ircode = list(self.builder.build([io.StringIO(snippet)])) - actualErrors = [err.row for err in self.diag.diags] - if rows != actualErrors: - self.diag.printErrors() - self.assertSequenceEqual(rows, actualErrors) - # self.assertFalse(all(ircode)) - - def expectOK(self, snippet): - """ Expect a snippet to be OK """ - ircode = list(self.builder.build(self.makeFileList(snippet))) - if len(self.diag.diags) > 0: - self.diag.printErrors() - self.assertTrue(all(ircode)) - self.assertEqual(0, len(self.diag.diags)) - return ircode - - def testPackage(self): - p1 = """module p1; - type int A; - """ - p2 = """module p2; - import p1; - var p1.A b; - """ - self.expectOK([p1, p2]) - - def testPackageMutual(self): - p1 = """module p1; - import p2; - type int A; - var p2.B b; - """ - p2 = """module p2; - import p1; - var p1.A a; - """ - self.expectOK([p1, p2]) - - def testConstant(self): - snip = """module C; - const int a = 2; - """ - i = self.expectOK(snip) - - @unittest.skip('Not checked yet') - def testConstantMutual(self): - snip = """module C; - const int a = b + 1; - const int b = a + 1; - function void f() - { - return b; - } - """ - i = self.expectOK(snip) - - def testPackageNotExists(self): - p1 = """module p1; - import p23; - """ - self.expectErrors(p1, [0]) - - def testFunctArgs(self): - snippet = """ - module testargs; - function void t2(int a, double b) - { - t2(2, 2); - t2(2); - t2(1, 1.2); - } - """ - self.expectErrors(snippet, [5, 6]) - - def testReturn(self): - snippet = """ - module testreturn; - function void t() - { - return; - } - """ - self.expectOK(snippet) - - def testReturn2(self): - snippet = """ - module testreturn; - function int t() - { - return 2; - } - """ - self.expectOK(snippet) - - def testExpressions(self): - snippet = """ - module test; - function void t(int a, double b) - { - var int a2; - var bool c; - - a2 = b * a; - c = a; - } - """ - self.expectErrors(snippet, [8, 9]) - - def testExpression1(self): - snippet = """ - module testexpr1; - function void t() - { - var int a, b, c; - a = 1; - b = a * 2 + a * a; - c = b * a - 3; - } - """ - self.expectOK(snippet) - - def testEmpty(self): - snippet = """ - module A - """ - self.expectErrors(snippet, [3]) - - def testEmpty2(self): - snippet = "" - self.expectErrors(snippet, [1]) - - def testRedefine(self): - snippet = """ - module test; - var int a; - var int b; - var int a; - """ - self.expectErrors(snippet, [5]) - - def testWhile(self): - snippet = """ - module tstwhile; - function void t() - { - var int i; - i = 0; - while (i < 1054) - { - i = i + 3; - } - } - """ - self.expectOK(snippet) - - def testWhile2(self): - snippet = """ - module tstwhile; - function void t() - { - while(true) - { - } - - while(false) - { - } - } - """ - self.expectOK(snippet) - - def testIf(self): - snippet = """ - module tstIFF; - function void t(int b) - { - var int a; - a = 2; - if (a > b) - { - if (a > 1337) - { - b = 2; - } - } - else - { - b = 1; - } - - return b; - } - """ - self.expectOK(snippet) - - def testAndCondition(self): - snippet = """ - module tst; - function void t() { - if (4 > 3 and 1 < 10) { - } - } - """ - self.expectOK(snippet) - - def testOrCondition(self): - snippet = """ - module tst; - function void t() { - if (3 > 4 or 3 < 10) { - } - } - """ - self.expectOK(snippet) - - def testNonBoolCondition(self): - snippet = """ - module tst; - function void t() { - if (3+3) { - } - } - """ - self.expectErrors(snippet, [4]) - - def testTypeDef(self): - snippet = """ - module testtypedef; - type int my_int; - function void t() - { - var my_int a; - var int b; - a = 2; - b = a + 2; - } - """ - self.expectOK(snippet) - - def testLocalVariable(self): - snippet = """ - module testlocalvar; - function void t() - { - var int a, b; - a = 2; - b = a + 2; - } - """ - self.expectOK(snippet) - - def testUnknownType(self): - snippet = """module testlocalvar; - function void t() - { - var int2 a; - } - """ - self.expectErrors(snippet, [4]) - - def testStruct1(self): - snippet = """ - module teststruct1; - function void t() - { - var struct {int x, y;} a; - a.x = 2; - a.y = a.x + 2; - } - """ - self.expectOK(snippet) - - def testStruct2(self): - """ Select struct member from non struct type """ - snippet = """ - module teststruct1; - function void t() { - var int a; - a.z = 2; - } - """ - self.expectErrors(snippet, [5]) - - def testStructCall(self): - snippet = """ - module teststruct1; - function void t() - { - var struct {int x, y;} a; - a.x(9); - } - """ - self.expectErrors(snippet, [6]) - - def testPointerType1(self): - snippet = """ - module testpointer1; - var int* pa; - function void t() - { - var int a; - pa = &a; - *pa = 22; - a = *pa + *pa * 8; - } - """ - self.expectOK(snippet) - - def testPointerType(self): - snippet = """ - module testpointer; - var int* pa, pb; - function void t(int a, double b) - { - var int a2; - a2 = a; // parameters cannot be escaped for now.. - pa = &a2; - pb = pa; - *pa = 22; - } - """ - self.expectOK(snippet) - - def testPointerTypeInCorrect(self): - snippet = """ - module testpointerincorrect; - var int* pa; - function void t(int a, double b) - { - pa = 2; // type conflict - pa = &a; - pa = &2; // No valid lvalue - &a = pa; // No valid lvalue - **pa = 22; // Cannot deref int - } - """ - self.expectErrors(snippet, [6, 8, 9, 10]) - - def testPointerTypeIr(self): - snippet = """ - module testptr_ir; - function void t() - { - var int* a; - a = cast<int*>(40); - *a = 2; - } - """ - self.expectOK(snippet) - - def testPointerTypeIr2(self): - snippet = """ - module testptr_ir; - type struct {int x,y;}* gpio; - function void t() - { - var gpio a; - a = cast<gpio>(40); - a->x = 2; - a->y = a->x - 14; - } - """ - self.expectOK(snippet) - - def testWrongCast(self): - snippet = """ - module testptr_ir; - type struct {int x,y;}* gpio; - function void t() - { - var gpio a; - *cast<gpio>(*a); - } - """ - self.expectErrors(snippet, [7]) - - def testComplexType(self): - snippet = """ - module testpointer; - type int my_int; - - type struct { - int x, y; - } point; - - type struct { - int mem1; - int memb2; - point P1; - } my_struct; - - type my_struct* my_sptr; - var int* pa; - - function void t(int a, int b, my_sptr x) - { - var my_struct *msp; - - var my_struct u, v; - var point *pt; - - pt = &msp->P1; - msp = x; - *pa = 22 + u.mem1 * v.memb2 - u.P1.x; - x->memb2 = *pa + a * b; - - msp->P1.x = a * x->P1.y; - } - """ - self.expectOK(snippet) - - -if __name__ == '__main__': - unittest.main()
--- a/test/testcg.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -import unittest -import ppci -from ppci.codegen import CodeGenerator -from ppci import ir -from ppci.target.target_list import thumb_target -from ppci.outstream import BinaryOutputStream - - -def genTestFunction(): - m = ir.Module('tst') - f = ir.Function('tst') - m.add_function(f) - return m, f, f.entry - - -class testCodeGeneration(unittest.TestCase): - def setUp(self): - self.cg = CodeGenerator(thumb_target) - - def testFunction(self): - s = BinaryOutputStream(ppci.objectfile.ObjectFile()) - m, f, bb = genTestFunction() - bb.addInstruction(ir.Exp(ir.Const(123))) - bb.addInstruction(ir.Jump(f.epiloog)) - obj = self.cg.generate(m, s) - self.assertTrue(obj) - - -class testArmCodeGeneration(unittest.TestCase): - def testStack(self): - s = BinaryOutputStream(ppci.objectfile.ObjectFile()) - cg = CodeGenerator(thumb_target) - m, f, bb = genTestFunction() - bb.addInstruction(ir.Move(ir.Mem(ir.Const(1)), ir.Const(22))) - bb.addInstruction(ir.Jump(f.epiloog)) - cg.generate(m, s) - #s.dump() - - -if __name__ == '__main__': - unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testdiagrameditor.py Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,74 @@ + +import unittest +import os +import sys +import time + +try: + otherpath = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'python','other')) + sys.path.insert(0, otherpath) + import diagrameditor + + from PyQt5.QtWidgets import QApplication, QGraphicsItem + from PyQt5.QtTest import QTest + from PyQt5.QtCore import Qt, QTimer, QMimeData, QPointF + from PyQt5.QtGui import QDropEvent + skip_it = False + + # When creating an app per testcase, this fails horribly.. + app = QApplication(sys.argv) +except ImportError as e: + skip_it = True + + +if 'LCFOSGUITESTS' not in os.environ: + skip_it = True + +class DiagramEditorTestCase(unittest.TestCase): + def setUp(self): + if skip_it: + self.skipTest('No qt5 or X server') + return + #print('Instance:', QApplication.instance()) + self.main = diagrameditor.Main() + self.main.show() + QTest.qWaitForWindowActive(self.main) + + def tearDown(self): + QTimer.singleShot(100, app.quit) + app.exec_() + + def cmdNewModel(self): + # Press ctrl+N: + QTest.keyClick(self.main, Qt.Key_N, Qt.ControlModifier) + + def dragItemIntoScene(self): + library = self.main.findChild(diagrameditor.LibraryWidget, 'LibraryWidget') + editor = self.main.findChild(diagrameditor.EditorGraphicsView, 'Editor') + #ilibrary. + QTest.mousePress(library, Qt.LeftButton) + print(editor, type(editor)) + QTest.mouseMove(editor) + QTest.mouseRelease(editor, Qt.LeftButton) + mimedata = QMimeData() + mimedata.setData('component/name', 'Block:blk'.encode('ascii')) + de = QDropEvent(QPointF(10, 10), Qt.CopyAction, mimedata, + Qt.LeftButton, + Qt.NoModifier) + editor.dropEvent(de) + + def resizePlacedItem(self): + i = self.main.findChild(QGraphicsItem, "sizer_top_right") + print(i) + + def testScenario1(self): + self.cmdNewModel() + self.dragItemIntoScene() + self.resizePlacedItem() + + def testB(self): + print('b') + + +if __name__ == '__main__': + unittest.main()
--- a/test/testemulation.py Fri Mar 07 17:10:21 2014 +0100 +++ b/test/testemulation.py Thu Feb 19 14:10:52 2015 +0100 @@ -4,76 +4,126 @@ import subprocess import socket import time +import shutil from testzcc import ZccBaseTestCase # Store testdir for safe switch back to directory: testdir = os.path.dirname(os.path.abspath(__file__)) +def tryrm(fn): + try: + os.remove(fn) + except OSError: + pass + +qemu_app = 'qemu-system-arm' + +def has_qemu(): + """ Determines if qemu is possible """ + if not hasattr(shutil, 'which'): + return False + return bool(shutil.which(qemu_app)) + + +def runQemu(kernel, machine='lm3s811evb'): + """ Runs qemu on a given kernel file """ + + if not has_qemu(): + return '' + tryrm('qemucontrol.sock') + tryrm('qemuserial.sock') + + # Listen to the control socket: + qemu_control_serve = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + qemu_control_serve.bind('qemucontrol.sock') + qemu_control_serve.listen(0) + + # Listen to the serial output: + qemu_serial_serve = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + qemu_serial_serve.bind('qemuserial.sock') + qemu_serial_serve.listen(0) + + args = [qemu_app, '-M', machine, '-m', '16M', + '-nographic', + '-kernel', kernel, + '-monitor', 'unix:qemucontrol.sock', + '-serial', 'unix:qemuserial.sock', + '-S'] + p = subprocess.Popen(args) + + #qemu_serial Give process some time to boot: + qemu_serial_serve.settimeout(3) + qemu_control_serve.settimeout(3) + qemu_serial, address_peer = qemu_serial_serve.accept() + qemu_control, address_peer = qemu_control_serve.accept() + + # Give the go command: + qemu_control.send('cont\n'.encode('ascii')) + + qemu_serial.settimeout(0.2) + + # Receive all data: + data = bytearray() + for i in range(400): + try: + data += qemu_serial.recv(1) + except socket.timeout as e: + break + data = data.decode('ascii') + # print(data) + + # Send quit command: + qemu_control.send("quit\n".encode('ascii')) + if hasattr(subprocess, 'TimeoutExpired'): + try: + p.wait(timeout=3) + except subprocess.TimeoutExpired: + p.kill() + else: + time.sleep(2) + p.kill() + qemu_control.close() + qemu_serial.close() + qemu_control_serve.close() + qemu_serial_serve.close() + + tryrm('qemucontrol.sock') + tryrm('qemuserial.sock') + + # Check that output was correct: + return data + class EmulationTestCase(ZccBaseTestCase): """ Tests the compiler driver """ def setUp(self): - if 'TESTEMU' not in os.environ: - self.skipTest('Not running emulation tests') - - def runQemu(self, kernel, machine='lm3s811evb'): - args = ['qemu-system-arm', '-M', machine, '-m', '16M', - '-nographic', '-kernel', kernel, '-monitor', - 'unix:qemucontrol.sock,server', - '-serial', 'unix:qemuserial.sock,server'] - p = subprocess.Popen(args) - - # Give process some time to boot: - time.sleep(0.5) - - # Connect to the control socket: - qemu_control = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - qemu_control.connect('qemucontrol.sock') - - time.sleep(0.5) - - # Now connect to the serial output: - qemu_serial = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - qemu_serial.connect('qemuserial.sock') - - time.sleep(0.5) - - qemu_serial.settimeout(0.2) - - # Receive all data: - data = bytearray() - for i in range(40): - try: - data += qemu_serial.recv(1) - except socket.timeout as e: - break - data = data.decode('ascii') - # print(data) - - # Send quit command: - qemu_control.send("quit\n".encode('ascii')) - p.wait(timeout=3) - qemu_control.close() - qemu_serial.close() - - # Check that output was correct: - return data + if not has_qemu(): + self.skipTest('Not running Qemu test') def testM3Bare(self): """ Build bare m3 binary and emulate it """ - recipe = os.path.join(testdir, 'm3_bare', 'recipe.yaml') + recipe = os.path.join(testdir, 'm3_bare', 'build.xml') self.buildRecipe(recipe) - data = self.runQemu('m3_bare/bare.bin') + data = runQemu('m3_bare/bare.bin') self.assertEqual('Hello worle', data) def testA9Bare(self): """ Build vexpress cortex-A9 binary and emulate it """ - recipe = os.path.join(testdir, '..', 'examples', 'qemu_a9_hello', 'recipe.yaml') + recipe = os.path.join(testdir, '..', 'examples', 'qemu_a9_hello', + 'build.xml') self.buildRecipe(recipe) - data = self.runQemu('../examples/qemu_a9_hello/hello.bin', machine='vexpress-a9') + data = runQemu('../examples/qemu_a9_hello/hello.bin', + machine='vexpress-a9') self.assertEqual('Hello worle', data) + def testKernelVexpressA9(self): + """ Build vexpress cortex-A9 binary and emulate it """ + recipe = os.path.join(testdir, '..', 'kernel', 'build.xml') + self.buildRecipe(recipe) + data = runQemu('../kernel/kernel_arm.bin', machine='vexpress-a9') + self.assertEqual('Welcome to lcfos!', data[:17]) + if __name__ == '__main__': unittest.main()
--- a/test/testgraph.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -#!/usr/bin/python - -import unittest -from ppci.codegen.graph import Graph, Node, DiGraph, DiNode -from ppci.codegen.interferencegraph import InterferenceGraph -from ppci.codegen.flowgraph import FlowGraph -from ppci import ir -from ppci.irmach import AbstractInstruction as AI -from ppci.target import Nop - - -class GraphTestCase(unittest.TestCase): - def testEdge(self): - g = Graph() - n1 = Node(g) - g.add_node(n1) - n2 = Node(g) - g.add_node(n2) - g.addEdge(n1, n2) - self.assertTrue(g.hasEdge(n2, n1)) - self.assertTrue(g.hasEdge(n1, n2)) - g.delNode(n1) - g.delNode(n2) - - def testDegree(self): - g = Graph() - n1 = Node(g) - g.add_node(n1) - n2 = Node(g) - g.add_node(n2) - n3 = Node(g) - g.add_node(n3) - g.addEdge(n1, n2) - g.addEdge(n1, n3) - self.assertEqual(2, n1.Degree) - self.assertEqual(1, n2.Degree) - g.delNode(n2) - self.assertEqual(1, n1.Degree) - - -class DigraphTestCase(unittest.TestCase): - def testSuccessor(self): - g = DiGraph() - a = DiNode(g) - b = DiNode(g) - c = DiNode(g) - g.add_node(a) - g.add_node(b) - g.add_node(c) - g.addEdge(a, b) - g.addEdge(b, c) - self.assertEqual({b}, a.Succ) - self.assertEqual({b}, c.Pred) - g.delNode(c) - self.assertEqual(set(), b.Succ) - - -class InterferenceGraphTestCase(unittest.TestCase): - def testNormalUse(self): - t1 = ir.Temp('t1') - t2 = ir.Temp('t2') - t3 = ir.Temp('t3') - t4 = ir.Temp('t4') - t5 = ir.Temp('t5') - t6 = ir.Temp('t6') - instrs = [] - instrs.append(AI(Nop, dst=[t1])) - instrs.append(AI(Nop, dst=[t2])) - instrs.append(AI(Nop, dst=[t3])) - cfg = FlowGraph(instrs) - ig = InterferenceGraph(cfg) - - def testCombine(self): - t1 = ir.Temp('t1') - t2 = ir.Temp('t2') - t3 = ir.Temp('t3') - t4 = ir.Temp('t4') - instrs = [] - instrs.append(AI(Nop, dst=[t1])) - instrs.append(AI(Nop, dst=[t2])) - instrs.append(AI(Nop, dst=[t3])) - instrs.append(AI(Nop, dst=[t4], src=[t3])) - instrs.append(AI(Nop, src=[t4])) - instrs.append(AI(Nop, src=[t1])) - instrs.append(AI(Nop, src=[t2])) - cfg = FlowGraph(instrs) - ig = InterferenceGraph(cfg) - ig.Combine(ig.getNode(t4), ig.getNode(t3)) - self.assertIs(ig.getNode(t4), ig.getNode(t3)) - - -if __name__ == '__main__': - unittest.main()
--- a/test/testhexedit.py Fri Mar 07 17:10:21 2014 +0100 +++ b/test/testhexedit.py Thu Feb 19 14:10:52 2015 +0100 @@ -1,16 +1,23 @@ import sys import unittest -import hexedit -#import ide +try: + import hexedit + #import ide -from PyQt5.QtWidgets import QApplication -from PyQt5.QtTest import QTest -from PyQt5.QtCore import Qt + from PyQt5.QtWidgets import QApplication + from PyQt5.QtTest import QTest + from PyQt5.QtCore import Qt + skip_it = False +except ImportError as e: + skip_it = True class HexEditorTest(unittest.TestCase): def setUp(self): + if skip_it: + self.skipTest('No qt5') + return self.app = QApplication(sys.argv) self.ui = hexedit.HexEditor() self.bv = self.ui.he.bv
--- a/test/testhexfile.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -import unittest -import io -from utils import HexFile, HexFileException - - -class testHexFile(unittest.TestCase): - def saveload(self, hf): - f = io.StringIO() - hf.save(f) - hf2 = HexFile() - hf2.load(io.StringIO(f.getvalue())) - self.assertEqual(hf, hf2) - - def testSave1(self): - hf = HexFile() - hf.addRegion(0x8000, bytes.fromhex('aabbcc')) - self.saveload(hf) - - def testSave2(self): - hf = HexFile() - hf.addRegion(0x8000, bytes.fromhex('aabbcc')) - hf.addRegion(0x118000, bytes.fromhex('aabbcc')) - self.saveload(hf) - - def testSave3(self): - hf = HexFile() - hf.addRegion(0x8000, bytes.fromhex('aabbcc')) - hf.addRegion(0xFFFE, bytes.fromhex('aabbcc')) - self.saveload(hf) - - @unittest.skip('Takes too long') - def testSave4(self): - hf = HexFile() - hf.addRegion(0xF000, bytes.fromhex('ab')*0x10000) - self.saveload(hf) - - @unittest.skip('Takes too long') - def testSave5(self): - hf = HexFile() - hf.addRegion(0xF003, bytes.fromhex('ab')*0x10000) - self.saveload(hf) - - def testTwoRegions(self): - hf = HexFile() - hf2 = HexFile() - hf.addRegion(0x100, bytes.fromhex('abcd')) - hf.addRegion(0x200, bytes.fromhex('beef')) - hf2.addRegion(0x200, bytes.fromhex('beef')) - hf2.addRegion(0x100, bytes.fromhex('abcd')) - self.assertEqual(hf, hf2) - - def testMerge(self): - hf = HexFile() - hf.addRegion(0x10, bytes.fromhex('abcdab')) - hf.addRegion(0x13, bytes.fromhex('abcdab')) - self.assertEqual(1, len(hf.regions)) - - def testOverlapped(self): - hf = HexFile() - hf.addRegion(0x10, bytes.fromhex('abcdab')) - with self.assertRaisesRegex(HexFileException, 'verlap'): - hf.addRegion(0x12, bytes.fromhex('abcdab')) - - def testEqual(self): - hf1 = HexFile() - hf2 = HexFile() - hf1.addRegion(10, bytes.fromhex('aabbcc')) - hf2.addRegion(10, bytes.fromhex('aabbcc')) - self.assertEqual(hf1, hf2) - - def testNotEqual(self): - hf1 = HexFile() - hf2 = HexFile() - hf1.addRegion(10, bytes.fromhex('aabbcc')) - hf2.addRegion(10, bytes.fromhex('aabbdc')) - self.assertNotEqual(hf1, hf2) - - def testNotEqual2(self): - hf1 = HexFile() - hf2 = HexFile() - hf1.addRegion(10, bytes.fromhex('aabbcc')) - hf2.addRegion(10, bytes.fromhex('aabbcc')) - hf2.addRegion(22, bytes.fromhex('aabbcc')) - self.assertNotEqual(hf1, hf2) - - def testLoad(self): - hf = HexFile() - dummyhex = """:01400000aa15""" - f = io.StringIO(dummyhex) - hf.load(f) - self.assertEqual(1, len(hf.regions)) - self.assertEqual(0x4000, hf.regions[0].address) - self.assertSequenceEqual(bytes.fromhex('aa'), hf.regions[0].data) - - def testIncorrectCrc(self): - hf = HexFile() - txt = ":01400000aabb" - f = io.StringIO(txt) - with self.assertRaisesRegex(HexFileException, 'crc'): - hf.load(f) - - def testIncorrectLength(self): - hf = HexFile() - txt = ":0140002200aabb" - f = io.StringIO(txt) - with self.assertRaisesRegex(HexFileException, 'count'): - hf.load(f) - -if __name__ == '__main__': - unittest.main()
--- a/test/testir.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -import unittest -import os -import sys -import io -import ppci -from ppci import ir -from ppci import irutils -from ppci.transform import ConstantFolder - - -class IrCodeTestCase(unittest.TestCase): - def testAdd(self): - v = ir.Add(ir.Const(1), ir.Const(2)) - - -class IrBuilderTestCase(unittest.TestCase): - def setUp(self): - self.b = irutils.Builder() - self.m = ir.Module('test') - self.b.setModule(self.m) - - def testBuilder(self): - f = self.b.newFunction('add') - self.b.setFunction(f) - bb = self.b.newBlock() - self.b.emit(ir.Jump(bb)) - self.b.setBlock(bb) - self.b.emit(ir.Exp(ir.Const(0))) - self.b.emit(ir.Jump(f.epiloog)) - # Run interpreter: - # r = self.m.getFunction('add').call(1, 2) - #self.assertEqual(3, r) - - -class PatternMatchTestCase(unittest.TestCase): - @unittest.skip('Not yet implemented') - def testSimpleTree(self): - t = ir.Term('x') - pat = ir.Binop(ir.Const(2), '+', t) - res, mp = ir.match_tree(ir.Binop(ir.Const(2), '+', 3), pat) - self.assertTrue(res) - self.assertIn(t, mp) - self.assertEqual(3, mp[t]) - - @unittest.skip('Not yet implemented') - def testSimpleTree2(self): - t = ir.Term('x') - t2 = ir.Term('y') - pat = ir.Binop(ir.Const(2), '+', ir.Binop(t, '-', t2)) - res, mp = ir.match_tree(ir.Binop(ir.Const(2), '+', ir.Binop(2,'-',3)), pat) - self.assertTrue(res) - self.assertIn(t, mp) - self.assertEqual(2, mp[t]) - self.assertIn(t2, mp) - self.assertEqual(3, mp[t2]) - res, mp = ir.match_tree(ir.Const(2), pat) - self.assertFalse(res) - - -class ConstantFolderTestCase(unittest.TestCase): - def setUp(self): - self.b = irutils.Builder() - self.cf = ConstantFolder() - self.m = ir.Module('test') - self.b.setModule(self.m) - - def testBuilder(self): - f = self.b.newFunction('test') - self.b.setFunction(f) - bb = self.b.newBlock() - self.b.emit(ir.Jump(bb)) - self.b.setBlock(bb) - v1 = ir.Const(5) - v2 = ir.Const(7) - v3 = ir.Add(v1, v2) - self.b.emit(ir.Jump(f.epiloog)) - self.cf.run(self.m) - - def testAdd0(self): - f = self.b.newFunction('test') - self.b.setFunction(f) - self.b.setBlock(self.b.newBlock()) - v1 = ir.Const(0) - v3 = ir.Add(v1, ir.Const(0)) - - -class TestWriter(unittest.TestCase): - def testWrite(self): - writer = irutils.Writer() - module = ir.Module('mod1') - function = ir.Function('func1', module) - f = io.StringIO() - writer.write(module, f) - #print(f.getvalue()) - f2 = io.StringIO(f.getvalue()) - reader = irutils.Reader() - module2 = reader.read(f2) - f = io.StringIO() - writer.write(module2, f) - #print(f.getvalue()) - - -class TestReader(unittest.TestCase): - def testAddExample(self): - reader = irutils.Reader() - with open('../examples/pi/add.pi') as f: - m = reader.read(f) - self.assertTrue(m) - #print(m) - - -if __name__ == '__main__': - unittest.main() - sys.exit()
--- a/test/testmsp430asm.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -#!/usr/bin/python - -import unittest -from ppci.assembler import tokenize, Assembler -from ppci.objectfile import ObjectFile -from ppci.outstream import BinaryOutputStream -from ppci.target.target_list import msp430target -from testasm import AsmTestCaseBase - -a = Assembler(msp430target) - -class Msp430AssemblerTestCase(AsmTestCaseBase): - def setUp(self): - self.t = msp430target - self.obj = ObjectFile() - self.ostream = BinaryOutputStream(self.obj) - self.ostream.selectSection('.text') - self.a = a - - def testMov(self): - self.feed("mov r14, r15") - self.check('0F4E') - - def testMov1337(self): - self.feed("mov 0x1337, r12") - self.check('3C403713') - - def testAdd(self): - self.feed("add r15, r13") - self.check('0D5F') - - def testReti(self): - self.feed("reti") - self.check('0013') - - -if __name__ == '__main__': - unittest.main()
--- a/test/testpyy.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,250 +0,0 @@ -import unittest -from pyyacc import Grammar, Item, ParserGenerationException, ParserException -from pyyacc import EPS, EOF, calculate_first_sets -from ppci import Token - - -class genTokens: - def __init__(self, lst): - def tokGen(): - for t in lst: - yield Token(t, t) - while True: - yield Token(EOF, EOF) - self.tokens = tokGen() - self.token = self.tokens.__next__() - - def next_token(self): - t = self.token - if t.typ != EOF: - self.token = self.tokens.__next__() - return t - - -class testLR(unittest.TestCase): - """ Test basic LR(1) parser generator constructs """ - def testSimpleGrammar(self): - # 1. define a simple grammar: - g = Grammar(['identifier', '(', ')', '+', '*']) - g.add_production('input', ['expression']) - g.add_production('expression', ['term']) - g.add_production('expression', ['expression', '+', 'term']) - g.add_production('term', ['factor']) - g.add_production('term', ['term', '*', 'factor']) - g.add_production('factor', ['(', 'expression', ')']) - g.add_production('factor', ['identifier']) - g.start_symbol = 'input' - # 2. define input: - tokens = genTokens(['identifier', '+', 'identifier', '+', 'identifier']) - # 3. build parser: - p = g.generate_parser() - # 4. feed input: - p.parse(tokens) - - def testReduceReduceConflict(self): - """ Check if a reduce-reduce conflict is detected """ - # Define a grammar with an obvious reduce-reduce conflict: - g = Grammar(['id']) - g.add_production('goal', ['a']) - g.add_production('a', ['b']) - g.add_production('a', ['c']) - g.add_production('b', ['id']) - g.add_production('c', ['id']) - g.start_symbol = 'goal' - with self.assertRaises(ParserGenerationException): - p = g.generate_parser() - - def testShiftReduceConflict(self): - """ Must be handled automatically by doing shift """ - g = Grammar([EOF, 'if', 'then', 'else', 'ass']) - # Ambiguous grammar: - g.add_production('if_stmt', ['if', 'then', 'stmt']) - g.add_production('if_stmt', ['if', 'then', 'stmt', 'else', 'stmt']) - g.add_production('stmt', ['if_stmt']) - g.add_production('stmt', ['ass']) - g.start_symbol = 'stmt' - p = g.generate_parser() - # Ambiguous program: - tokens = genTokens(['if', 'then','if', 'then', 'ass', 'else', 'ass']) - p.parse(tokens) - - def testUndefinedTerminal(self): - """ Test correct behavior when a terminal is undefined """ - g = Grammar(['b']) - g.add_production('goal', ['a']) - g.add_production('a', ['b']) - g.add_production('a', ['c']) - g.start_symbol = 'goal' - with self.assertRaises(ParserGenerationException): - g.generate_parser() - - def testRedefineTerminal(self): - """ Test correct behavior when a terminal is redefined """ - g = Grammar([EOF, 'b', 'c']) - g.add_production('goal', ['a']) - with self.assertRaises(ParserGenerationException): - g.add_production('b', ['c']) # Not allowed - g.add_production('a', ['c']) - g.start_symbol = 'goal' - g.generate_parser() - - def testEmpty(self): - """ Test empty token stream """ - g = Grammar([',']) - g.add_production('input', [',']) - g.start_symbol = 'input' - p = g.generate_parser() - tokens = genTokens([]) - with self.assertRaises(ParserException): - p.parse(tokens) - - def testEps(self): - """ Test epsilon terminal """ - g = Grammar(['a', 'b']) - g.add_production('input', ['optional_a', 'b']) - g.add_production('optional_a', ['a']) - g.add_production('optional_a', []) - g.start_symbol = 'input' - p = g.generate_parser() - tokens = genTokens(['b']) - p.parse(tokens) - - def testEps2(self): - g = Grammar(['id', ':']) - g.add_production('input', ['opt_lab', 'ins', 'op1']) - g.add_production('input', ['ins', 'op1']) - g.add_production('opt_lab', ['id', ':']) - g.add_production('ins', ['id']) - g.add_production('op1', ['id']) - g.start_symbol = 'input' - p = g.generate_parser() - tokens = genTokens(['id', ':', 'id', 'id']) # i.e. "lab_0: inc rax" - p.parse(tokens) - tokens = genTokens(['id', 'id']) # i.e. "inc rax" - p.parse(tokens) - - def testEpsSequence(self): - """ Test epsilon terminal for use in sequences """ - g = Grammar(['a']) - g.add_production('aas', []) - g.add_production('aas', ['aas', 'a']) - g.start_symbol = 'aas' - p = g.generate_parser() - tokens = genTokens(['a', 'a', 'a']) - p.parse(tokens) - tokens = genTokens([]) - p.parse(tokens) - - def test_cb(self): - """ Test callback of one rule and order or parameters """ - self.cb_called = False - def cb(a, c, b): - self.cb_called = True - self.assertEqual(a.val, 'a') - self.assertEqual(b.val, 'b') - self.assertEqual(c.val, 'c') - g = Grammar(['a', 'b', 'c']) - g.add_production('goal', ['a', 'c', 'b'], cb) - g.start_symbol = 'goal' - p = g.generate_parser() - tokens = genTokens(['a', 'c', 'b']) - p.parse(tokens) - self.assertTrue(self.cb_called) - - -class testExpressionGrammar(unittest.TestCase): - def setUp(self): - g = Grammar(['EOF', 'identifier', '(', ')', '+', '*', 'num']) - g.add_production('input', ['expression']) - g.add_production('expression', ['term']) - g.add_production('expression', ['expression', '+', 'term']) - g.add_production('term', ['factor']) - g.add_production('term', ['term', '*', 'factor']) - g.add_production('factor', ['(', 'expression', ')']) - g.add_production('factor', ['identifier']) - g.add_production('factor', ['num']) - g.start_symbol = 'input' - self.g = g - - def testFirstSimpleGrammar(self): - # 1. define a simple grammar: - first = calculate_first_sets(self.g) - self.assertEqual(first['input'], {'identifier', '(', 'num'}) - self.assertEqual(first['term'], {'identifier', '(', 'num'}) - - def testCanonical(self): - s0 = self.g.initialItemSet() - s, gt = self.g.genCanonicalSet(s0) - # Must result in 12 sets: - self.assertEqual(len(s), 24) - - -class testParserGenerator(unittest.TestCase): - """ Tests several parts of the parser generator """ - def setUp(self): - g = Grammar(['(', ')']) - g.add_production('goal', ['list']) - g.add_production('list', ['list', 'pair']) - g.add_production('list', ['pair']) - g.add_production('pair', ['(', 'pair', ')']) - g.add_production('pair', ['(', ')']) - g.start_symbol = 'goal' - self.g = g - - def testFirstSet(self): - for a in ['(', ')', EOF, 'EPS']: - self.assertEqual(self.g.first[a], {a}) - for nt in ['list', 'pair', 'goal']: - self.assertEqual(self.g.first[nt], {'('}) - - def testInitItemSet(self): - p0, p1, p2, p3, p4 = self.g.productions - s0 = self.g.initialItemSet() - self.assertEqual(len(s0), 9) # 9 with the goal rule included! - self.assertIn(Item(p0, 0, EOF), s0) - self.assertIn(Item(p1, 0, EOF), s0) - self.assertIn(Item(p1, 0, '('), s0) - self.assertIn(Item(p2, 0, EOF), s0) - self.assertIn(Item(p2, 0, '('), s0) - self.assertIn(Item(p3, 0, EOF), s0) - self.assertIn(Item(p3, 0, '('), s0) - self.assertIn(Item(p4, 0, EOF), s0) - self.assertIn(Item(p4, 0, '('), s0) - - def testCanonical(self): - s0 = self.g.initialItemSet() - s, gt = self.g.genCanonicalSet(s0) - # Must result in 12 sets: - self.assertEqual(len(s), 12) - - def testClosure(self): - p0, p1, p2, p3, p4 = self.g.productions - s0 = set() - s0.add(Item(p0, 0, EOF)) - self.assertEqual(len(s0), 1) # 1 rule - self.assertIn(Item(p0, 0, EOF), s0) - - # Invoke closure on set: - s0 = self.g.closure(s0) - self.assertIn(Item(p0, 0, EOF), s0) - self.assertIn(Item(p1, 0, EOF), s0) - self.assertIn(Item(p1, 0, '('), s0) - self.assertIn(Item(p2, 0, EOF), s0) - self.assertIn(Item(p2, 0, '('), s0) - self.assertIn(Item(p3, 0, EOF), s0) - self.assertIn(Item(p3, 0, '('), s0) - self.assertIn(Item(p4, 0, EOF), s0) - self.assertIn(Item(p4, 0, '('), s0) - - def testParser(self): - tokens = ['(', '(', ')', ')', '(', ')'] - # 3. build parser: - p = self.g.generate_parser() - self.assertEqual(len(p.goto_table), 5) - self.assertEqual(len(p.action_table), 19) - - # 4. feed input: - p.parse(genTokens(tokens)) - -if __name__ == '__main__': - unittest.main()
--- a/test/testregalloc.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -import unittest -import os -import sys -from ppci.irmach import AbstractInstruction as makeIns, Frame -from ppci.codegen.registerallocator import RegisterAllocator -from ppci import ir -from ppci.target import Nop - - -class RegAllocTestCase(unittest.TestCase): - def setUp(self): - self.ra = RegisterAllocator() - - def testRegAlloc(self): - f = Frame('tst') - f.regs = [1,2,3,4,5,6] # for test use numbers! - f.tempMap = {} - t1 = ir.Temp('t1') - t2 = ir.Temp('t2') - t3 = ir.Temp('t3') - t4 = ir.Temp('t4') - t5 = ir.Temp('t5') - f.instructions.append(makeIns(Nop, dst=[t1])) - f.instructions.append(makeIns(Nop, dst=[t2])) - f.instructions.append(makeIns(Nop, dst=[t3])) - f.instructions.append(makeIns(Nop, dst=[t4], src=[t1, t2])) - f.instructions.append(makeIns(Nop, dst=[t5], src=[t4, t3])) - f.instructions.append(makeIns(Nop, src=[t5])) - self.ra.allocFrame(f) - self.conflict(t1, t2) - self.conflict(t2, t3) - - def conflict(self, ta, tb): - self.assertNotEqual(self.ra.Node(ta).color, self.ra.Node(tb).color) - - def testRegCoalesc(self): - f = Frame('tst') - f.regs = [1,2,3,4,5,6] # for test use numbers! - f.tempMap = {} - t1 = ir.Temp('t1') - t2 = ir.Temp('t2') - t3 = ir.Temp('t3') - t4 = ir.Temp('t4') - t5 = ir.Temp('t5') - t6 = ir.Temp('t6') - f.instructions.append(makeIns(Nop, dst=[t1])) - f.instructions.append(makeIns(Nop, dst=[t2])) - f.instructions.append(makeIns(Nop, dst=[t3])) - f.instructions.append(makeIns(Nop, dst=[t4], src=[t2, t1])) - f.instructions.append(makeIns(Nop, dst=[t5], src=[t3])) - f.instructions.append(makeIns(Nop, dst=[t5], src=[t4, t5])) - f.instructions.append(makeIns(Nop, dst=[t6], src=[t5])) - f.instructions.append(makeIns(Nop, src=[t6])) - self.ra.allocFrame(f) - self.conflict(t1, t2) - self.conflict(t2, t3) - self.conflict(t1, t3) - -if __name__ == '__main__': - unittest.main() -
--- a/test/testthumbasm.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,151 +0,0 @@ -import unittest -from ppci.outstream import BinaryOutputStream -from ppci.objectfile import ObjectFile -from asm import Assembler -from testasm import AsmTestCaseBase -from ppci.target.target_list import thumb_target - -a = Assembler(thumb_target) - -class ThumbAssemblerTestCase(AsmTestCaseBase): - def setUp(self): - self.t = thumb_target - self.obj = ObjectFile() - self.ostream = BinaryOutputStream(self.obj) - self.ostream.selectSection('.text') - self.a = a # - - def testMovImm8(self): - self.feed('mov r4, 100') - self.check('6424') - - @unittest.skip - def testMovExt(self): - self.feed('mov r3, sp') - self.check('') - - def testYield(self): - self.feed('yield') - self.check('10bf') - - def testPush(self): - self.feed('push {r2,r3,lr}') - self.check('0cb5') - - def testPop(self): - self.feed('pop {r4-r6, pc}') - self.check('70bd') - - def testStr5(self): - self.feed('str r4, [r1 + 0]') - self.check('0c60') - - def testLdr5(self): - self.feed('ldr r4, [r0 + 0]') - self.check('0468') - - def testLdrSpRel(self): - self.feed('ldr r0, [sp + 4]') - self.check('0198') - - def testStrSpRel(self): - self.feed('str r0, [sp + 4]') - self.check('0190') - - def testLdrPcRel(self): - self.feed('ldr r7, henkie') - self.feed('ldr r6, henkie') - self.feed('ldr r1, henkie') - self.feed('align 4') - self.feed('dcd 1') - self.feed('henkie: dcd 2') - self.check('024F024E 01490000 01000000 02000000') - - def testBranch(self): - self.feed('start: b henkie') - self.feed('beq henkie') - self.feed('bne henkie') - self.feed('henkie: b start') - self.feed('eof: b eof') - self.check('01e000d0 ffd1fbe7 fee7') - - def testConditions(self): - self.feed('blt x') - self.feed('bgt x') - self.feed('x:') - self.check('00dbffdc') - - def testBoff(self): - self.feed('b henkie') - self.feed('b henkie') - self.feed('b henkie') - self.feed('b henkie') - self.feed('b henkie') - self.feed('b henkie') - self.feed('b henkie') - self.feed('henkie:') - self.feed('b henkie') - self.feed('b henkie') - self.feed('b henkie') - self.feed('b henkie') - self.check('05e004e0 03e002e0 01e000e0 ffe7fee7 fde7fce7 fbe7') - - def testBl(self): - self.feed('bl henkie') - self.feed('bl henkie') - self.feed('henkie:') - self.feed('bl henkie') - self.feed('bl henkie') - self.check('00f0 02f8 00f0 00f8 fff7 feff fff7 fcff') - - def testCmpRegReg(self): - self.feed('cmp r0, r1') - self.check('8842') - - def testAddimm3(self): - self.feed('add r3, r5, 2') - self.feed('add r4, r1, 6') - self.check('ab1c8c1d') - - def testSubImm3(self): - self.feed('sub r3, r5, 2') - self.feed('sub r4, r1, 6') - self.check('ab1e8c1f') - - def testLeftShift(self): - self.feed('lsl r3, r5') - self.check('ab40') - - def testAddSp(self): - self.feed('add sp,sp,8') - self.feed('add sp,sp,16') - self.check('02b004b0') - - def testSubSp(self): - self.feed('sub sp,sp,32') - self.feed('sub sp,sp,4') - self.check('88b081b0') - - def testSequence1(self): - self.feed('mov r5, 3') - self.feed('add r4, r5, 0') - self.feed('loop: add r6, r4, 7') - self.feed('cmp r6, 5') - self.check('0325 2c1c e61d 052e') - - def testSequence2(self): - self.feed('henkie:') - self.feed('push {r1,r4,r5}') - self.feed('add r5, r2, r4') - self.feed('cmp r4, r2') - self.feed('ldr r0, [sp + 4]') - self.feed('str r3, [sp + 16]') - self.feed('pop {r1, r4, r5}') - self.feed('lsl r3, r4') - self.feed('cmp r3, r5') - self.feed('beq henkie') - self.feed('bne henkie') - self.feed('b henkie') - self.check('32b41519 94420198 049332bc a340ab42 f6d0f5d1 f4e7') - -
--- a/test/testx86asm.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +0,0 @@ -#!/usr/bin/python - -import unittest -from testasm import AsmTestCaseBase - - -class AssemblerTestCase(AsmTestCaseBase): - """ - test methods start with 'test*' - Checks several assembly constructs agains their bytecodes - """ - def setUp(self): - self.skipTest('not implemented yet') - self.assembler = Assembler('x86-64') - a = Assembler() - - @unittest.skip - def testX86(self): - self.feed('mov rax, rbx') # ; 0x48, 0x89, 0xd8 - self.feed('xor rcx, rbx') # ; 0x48, 0x31, 0xd9 - self.feed('inc rcx') # ; 0x48 0xff 0xc1 - self.check('48 89 d8 48 31 d9 48 ff c1') - - def tstAssembler(self): - """ Check all kind of assembler cases """ - assert(assembler.shortjump(5) == [0xeb, 0x5]) - assert(assembler.shortjump(-2) == [0xeb, 0xfc]) - assert(assembler.shortjump(10,'GE') == [0x7d, 0xa]) - assert(assembler.nearjump(5) == [0xe9, 0x5,0x0,0x0,0x0]) - assert(assembler.nearjump(-2) == [0xe9, 0xf9, 0xff,0xff,0xff]) - assert(assembler.nearjump(10,'LE') == [0x0f, 0x8e, 0xa,0x0,0x0,0x0]) - - def testCall(self): - self.feed('call r10') - self.check('') - self.feed('call rcx') - - # assert(assembler.call('r10') == [0x41, 0xff, 0xd2]) - # assert(assembler.call('rcx') == [0xff, 0xd1]) - - def testXOR(self): - assert(assembler.xorreg64('rax', 'rax') == [0x48, 0x31, 0xc0]) - assert(assembler.xorreg64('r9', 'r8') == [0x4d, 0x31, 0xc1]) - assert(assembler.xorreg64('rbx', 'r11') == [0x4c, 0x31, 0xdb]) - - def testINC(self): - assert(assembler.increg64('r11') == [0x49, 0xff, 0xc3]) - assert(assembler.increg64('rcx') == [0x48, 0xff, 0xc1]) - - def testPush(self): - assert(assembler.push('rbp') == [0x55]) - assert(assembler.push('rbx') == [0x53]) - assert(assembler.push('r12') == [0x41, 0x54]) - - def testPop(self): - self.feed('pop rbx') - self.feed('pop rbp') - self.feed('pop r12') - assert(assembler.pop('rbx') == [0x5b]) - assert(assembler.pop('rbp') == [0x5d]) - assert(assembler.pop('r12') == [0x41, 0x5c]) - - def testAsmLoads(self): - # TODO constant add testcases - assert(assembler.mov('rbx', 'r14') == [0x4c, 0x89, 0xf3]) - assert(assembler.mov('r12', 'r8') == [0x4d, 0x89, 0xc4]) - assert(assembler.mov('rdi', 'rsp') == [0x48, 0x89, 0xe7]) - - def testAsmMemLoads(self): - assert(assembler.mov('rax', ['r8','r15',0x11]) == [0x4b,0x8b,0x44,0x38,0x11]) - assert(assembler.mov('r13', ['rbp','rcx',0x23]) == [0x4c,0x8b,0x6c,0xd,0x23]) - - assert(assembler.mov('r9', ['rbp',-0x33]) == [0x4c,0x8b,0x4d,0xcd]) - #assert(assembler.movreg64('rbx', ['rax']) == [0x48, 0x8b,0x18]) - - assert(assembler.mov('rax', [0xb000]) == [0x48,0x8b,0x4,0x25,0x0,0xb0,0x0,0x0]) - assert(assembler.mov('r11', [0xa0]) == [0x4c,0x8b,0x1c,0x25,0xa0,0x0,0x0,0x0]) - - assert(assembler.mov('r11', ['RIP', 0xf]) == [0x4c,0x8b,0x1d,0x0f,0x0,0x0,0x0]) - - def testAsmMemStores(self): - assert(assembler.mov(['rbp', 0x13],'rbx') == [0x48,0x89,0x5d,0x13]) - assert(assembler.mov(['r12', 0x12],'r9') == [0x4d,0x89,0x4c,0x24,0x12]) - assert(assembler.mov(['rcx', 0x11],'r14') == [0x4c,0x89,0x71,0x11]) - - - assert(assembler.mov([0xab], 'rbx') == [0x48,0x89,0x1c,0x25,0xab,0x0,0x0,0x0]) - assert(assembler.mov([0xcd], 'r13') == [0x4c,0x89,0x2c,0x25,0xcd,0x0,0x0,0x0]) - - assert(assembler.mov(['RIP', 0xf], 'r9') == [0x4c,0x89,0x0d,0x0f,0x0,0x0,0x0]) - - def testAsmMOV8(self): - assert(assembler.mov(['rbp', -8], 'al') == [0x88, 0x45, 0xf8]) - assert(assembler.mov(['r11', 9], 'cl') == [0x41, 0x88, 0x4b, 0x09]) - - assert(assembler.mov(['rbx'], 'al') == [0x88, 0x03]) - assert(assembler.mov(['r11'], 'dl') == [0x41, 0x88, 0x13]) - - def testAsmLea(self): - assert(assembler.leareg64('r11', ['RIP', 0xf]) == [0x4c,0x8d,0x1d,0x0f,0x0,0x0,0x0]) - assert(assembler.leareg64('rsi', ['RIP', 0x7]) == [0x48,0x8d,0x35,0x07,0x0,0x0,0x0]) - - assert(assembler.leareg64('rcx', ['rbp', -8]) == [0x48,0x8d,0x4d,0xf8]) - - def testAssemblerCMP(self): - assert(assembler.cmpreg64('rdi', 'r13') == [0x4c, 0x39, 0xef]) - assert(assembler.cmpreg64('rbx', 'r14') == [0x4c, 0x39, 0xf3]) - assert(assembler.cmpreg64('r12', 'r9') == [0x4d, 0x39, 0xcc]) - - assert(assembler.cmpreg64('rdi', 1) == [0x48, 0x83, 0xff, 0x01]) - assert(assembler.cmpreg64('r11', 2) == [0x49, 0x83, 0xfb, 0x02]) - - def testAssemblerADD(self): - assert(assembler.addreg64('rbx', 'r13') == [0x4c, 0x01, 0xeb]) - assert(assembler.addreg64('rax', 'rbx') == [0x48, 0x01, 0xd8]) - assert(assembler.addreg64('r12', 'r13') == [0x4d, 0x01, 0xec]) - - assert(assembler.addreg64('rbx', 0x13) == [0x48, 0x83, 0xc3, 0x13]) - assert(assembler.addreg64('r11', 0x1234567) == [0x49, 0x81, 0xc3, 0x67, 0x45,0x23,0x1]) - assert(assembler.addreg64('rsp', 0x33) == [0x48, 0x83, 0xc4, 0x33]) - - def testAssemblerSUB(self): - assert(assembler.subreg64('rdx', 'r14') == [0x4c, 0x29, 0xf2]) - assert(assembler.subreg64('r15', 'rbx') == [0x49, 0x29, 0xdf]) - assert(assembler.subreg64('r8', 'r9') == [0x4d, 0x29, 0xc8]) - - assert(assembler.subreg64('rsp', 0x123456) == [0x48, 0x81, 0xec, 0x56,0x34,0x12,0x0]) - assert(assembler.subreg64('rsp', 0x12) == [0x48, 0x83, 0xec, 0x12]) - - def testAssemblerIDIV(self): - assert(assembler.idivreg64('r11') == [0x49, 0xf7, 0xfb]) - assert(assembler.idivreg64('rcx') == [0x48, 0xf7, 0xf9]) - assert(assembler.idivreg64('rsp') == [0x48, 0xf7, 0xfc]) - - def testAssemblerIMUL(self): - assert(assembler.imulreg64_rax('rdi') == [0x48, 0xf7, 0xef]) - assert(assembler.imulreg64_rax('r10') == [0x49, 0xf7, 0xea]) - assert(assembler.imulreg64_rax('rdx') == [0x48, 0xf7, 0xea]) - - assert(assembler.imulreg64('r11', 'rdi') == [0x4c, 0xf, 0xaf, 0xdf]) - assert(assembler.imulreg64('r12', 'rbx') == [0x4c, 0xf, 0xaf, 0xe3]) - # nasm generates this machine code: 0x4d, 0x6b, 0xff, 0xee - # This also works: 4D0FAFFE (another variant?? ) - assert(assembler.imulreg64('r15', 'r14') == [0x4d, 0x0f, 0xaf, 0xfe]) - - -if __name__ == '__main__': - unittest.main() -
--- a/test/testzcc.py Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,120 +0,0 @@ -import unittest -import os -import sys - -import zcc -from ppci.objectfile import ObjectFile -import ppci -import io -from ppci.target import target_list - -# Store testdir for safe switch back to directory: -testdir = os.path.dirname(os.path.abspath(__file__)) - -def relpath(*args): - return os.path.join(testdir, *args) - -class ZccBaseTestCase(unittest.TestCase): - def callZcc(self, arg_list): - parser = zcc.make_parser() - arg_list = ['--log', 'warn'] + arg_list - args = parser.parse_args(arg_list) - self.assertEqual(0, zcc.main(args)) - - def buildRecipe(self, recipe): - arg_list = ['recipe', recipe] - self.callZcc(arg_list) - - -class ZccTestCase(ZccBaseTestCase): - """ Tests the compiler driver """ - def setUp(self): - os.chdir(testdir) - - def tearDown(self): - os.chdir(testdir) - - def do(self, filenames, imps=[], extra_args=[]): - basedir = os.path.join('..', 'examples', 'c3') - arg_list = ['compile'] - arg_list += [os.path.join(basedir, fn) for fn in filenames] - for fn in imps: - arg_list.append('-i') - arg_list.append(os.path.join(basedir, fn)) - arg_list.append('--target') - arg_list.append('thumb') - arg_list += extra_args - self.callZcc(arg_list) - - - def testDumpIr(self): - basedir = relpath('..', 'examples', 'c3', 'comments.c3') - arg_list = ['compile', basedir] - arg_list.append('--target') - arg_list.append('thumb') - self.callZcc(arg_list) - - def testKernel(self): - """ Build kernel using zcc: """ - recipe = relpath('..', 'kernel', 'recipe.yaml') - self.buildRecipe(recipe) - - @unittest.skip('Too difficult to fix') - def testKernelBuildsEqualTwice(self): - """ Build kernel two times and check the output is equal """ - recipe = relpath('..', 'kernel', 'recipe.yaml') - bin_filename = relpath('..', 'kernel', 'kernel.bin') - self.buildRecipe(recipe) - with open(bin_filename, 'rb') as f: - a = f.read() - self.buildRecipe(recipe) - with open(bin_filename, 'rb') as f: - b = f.read() - self.assertSequenceEqual(a, b) - - def testUser(self): - """ Build userspace using zcc: """ - recipe = relpath('..', 'user', 'recipe.yaml') - self.buildRecipe(recipe) - - def testBurn2(self): - self.do(['burn2.c3'], ['stm32f4xx.c3']) - - def testBurn2_recipe(self): - recipe = relpath('..', 'examples', 'c3', 'recipe.yaml') - self.buildRecipe(recipe) - - def test_hello_A9_c3_recipe(self): - recipe = relpath('..', 'examples', 'qemu_a9_hello', 'recipe.yaml') - self.buildRecipe(recipe) - - @unittest.skip('Skip because of logfile') - def testBurn2WithLogging(self): - self.do(['burn2.c3'], ['stm32f4xx.c3'], extra_args=['--report', 'x.rst']) - - def testCommentsExample(self): - self.do(['comments.c3']) - - def testCast(self): - self.do(['cast.c3']) - - def testFunctions(self): - self.do(['functions.c3']) - - @unittest.skip('Revise this test') - def testSectionAddress(self): - src = """module tst; - function void t2() {var int t3; t3 = 2;} - """ - f = io.StringIO(src) - out = ObjectFile() - tg = target_list.armtarget - tr = ppci.tasks.TaskRunner() - tr.add_task(ppci.buildtasks.Compile([f], [], tg, out)) - tr.run_tasks() - code = out.get_section('code') - self.assertEqual(0x0, code.address) - - -if __name__ == '__main__': - unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/app.mmap Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,9 @@ + +MEMORY flash LOCATION=0x0 SIZE=0x10000 { + SECTION(code) +} + +MEMORY ram LOCATION=0x20000000 SIZE=0x10000 { + SECTION(data) +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/build.xml Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,32 @@ + +<project name="Userspace" default="all"> + <target name="all" depends="hello,init"> + </target> + + <target name="hello"> + <compile sources="hello/hello.c3;lib/*.c3" + target="arm" + output="obj/hello.o"/> + <link output="obj/hello.elf" layout="app.mmap" + target="arm" + objects="obj/hello.o"/> + </target> + + <target name="init"> + <compile sources="init/init.c3;lib/*.c3" + target="arm" + output="obj/init.o"/> + + <link + output="obj/init.elf" + layout="app.mmap" + target="arm" + objects="obj/init.o" /> + + <objcopy objectfile="obj/init.elf" + imagename="flash" + format="bin" + output="obj/init.bin" /> + </target> +</project> +
--- a/user/hello.c3 Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -module hello; -import lib; - -/* - Demo program running in userspace. -*/ - -function void start() -{ - lib.print(9); // 'Hello space'); -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/hello/hello.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,12 @@ +module hello; +import lib; + +/* + Demo program running in userspace. +*/ + +function void start() +{ + lib.print("Hello space"); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/init/init.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,12 @@ +module init; +import lib; + +/* +Initial program run by the system. +*/ + +function void start() +{ + lib.print("Welcome to lcfos"); +} +
--- a/user/ipc.c3 Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ - -module ipc; - -type struct { - int sender; - int cmd; - int data1; - int data2; - int data3; - int data4; -} Msg; - -const int MSG_SEND=1; -const int MSG_RECV=2; - -function int kernelTrap(int msgId, int a, int b) -{ - // TODO: make this in assembler? -} - -function void SendMessage(Msg *msg) -{ - kernelTrap(MSG_SEND, 1, 0) -} - -function void receive_message(Msg *msg) -{ - kernelTrap(MSG_RECV, 2, 0); -} -
--- a/user/lib.c3 Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -module lib; -import ipc; - -/* -Runtime library. - -*/ - -function void print(int txt) -{ - // TODO - var ipc.Msg msg; - ipc.SendMessage(&msg); -} - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/lib/ipc.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,32 @@ + +module ipc; + +type struct { + int sender; + int cmd; + int data1; + int data2; + int data3; + int data4; +} Msg; + +const int MSG_SEND=1; +const int MSG_RECV=2; + +function int kernelTrap(int msgId, int a, int b) +{ + // TODO: make this in assembler? +} + +function void SendMessage(Msg *msg) +{ + var int x; + x=kernelTrap(MSG_SEND, 1, 0) +} + +function void receive_message(Msg *msg) +{ + var int x; + x=kernelTrap(MSG_RECV, 2, 0); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/lib/lib.c3 Thu Feb 19 14:10:52 2015 +0100 @@ -0,0 +1,33 @@ +module lib; +import ipc; + +/* +Runtime library. +*/ + +// Hack until something better exists: +function void putc(int c) +{ + var int *UART0DR; + UART0DR = cast<int*>(0x109000); // UART0 DR register when remapped at 1MB + *UART0DR = c; +} + +function void print(string txt) +{ + // TODO + var ipc.Msg msg; + ipc.SendMessage(&msg); + + // TBD: send text to putc or via send message?? + var int i; + i = 0; + + while (i < txt->len) + { + putc(cast<int>(txt->txt[i])); + i = i + 1; + } +} + +
--- a/user/recipe.yaml Fri Mar 07 17:10:21 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ - -apps: - - link: - inputs: - - compile: - sources: [lib.c3, ipc.c3, hello.c3] - includes: [] - machine: thumb - output: kernel.elf2 - layout: - code: 0x0 - data: 0x20000000 - output: 'hello.bin' -
--- a/util/test_patterns.txt Fri Mar 07 17:10:21 2014 +0100 +++ b/util/test_patterns.txt Thu Feb 19 14:10:52 2015 +0100 @@ -4,10 +4,14 @@ mov sp, #0x6000 === mov r3, sp +mov pc, lr +mov pc, r2 +mov sp, r4 +mov r5, r6 === yield === -push {r11,r5,r4,lr} +push {r4, r5, r11, lr} === pop {r4,r5,r6} === @@ -19,4 +23,45 @@ str r9, [r2, #33] === ldr r5, [r3, #87] +=== +ldr r5, lab1 +ldr r11, lab1 +ldr r10, lab1 +lab1: +.word 0x12345566 +=== +cmp r4, r11 +cmp r5, #0x50000 +=== +adr r5, cval +adr r9, cval +adr r8, cval +cval: +adr r11, cval +adr r12, cval +adr r1, cval +pop {r2} +=== +lsl r11, r5, r3 +lsl r4, r8, r6 +=== +lsr r9, r0, r2 +lsr r4, r8, r6 +=== +and r9, r0, r2 +and r4, r8, r6 +=== +mcr p15, 0, r1, c2, c0, 0 +mcr p14, 0, r1, c8, c7, 0 +=== +mrc p15, 0, r1, c2, c0, 0 +mrc p14, 0, r1, c8, c7, 0 +=== +ldr r8, =a +a: +=== +ldrb r5, [r3, #87] +=== +strb r2, [r8, #11] +