Shop OBEX P1 Docs P2 Docs Learn Events
LLVM Backend for Propeller 2 - Page 13 — Parallax Forums

LLVM Backend for Propeller 2

1789101113»

Comments

  • Is long long (64) bit supported?

    I have this code that generates two different answers and don't know if this is correct:

    int main(int argc, char** argv)
    {
        long long t;
        int d1;
        unsigned int c5;
        int x;
    
        printf("Starting\n");
    
        x = rand() % 10;
        printf("Rand: %d\n", x);
    
        d1 = 8052495 + x;
        c5 = 31559;
    
        t = d1 - (c5 << 8);
        x = d1 - (c5 << 8);
    
        printf("t: %lld, x: %d\n", t, x);
    
        printf("Done\n");
    
        while (1)
        {
            wait(500);
        }
    }
    

    The long value is 4294941851 were the int value is -25445 which is the answer I was looking for.

    Mike

  • 64 bit ints and doubles should work, but I haven't tested them thoroughly, so if you find bugs, let me know, but I've been using them and having found any issues yet. Many floating point operations aren't supported yet (the code is there, I just need to actually enable compiling it into the library. I've been doing it piece by piece to avoid adding a ton of code at once and then hunting down bugs later).

  • @iseries I just merged in a rewrite of stdio and your SD card code. I haven't tested the sd stuff, and the changes to stdio shouldn't be breaking anything, EXCEPT I removed simple_printf. After the rewrite of printf, it didn't really makes sense to keep simple_printf since it was only slightly smaller and faster, not enough to keep both implementations. Let me know if you run into any issues.

  • Yes, the SD card functions has a bug in it and I didn't know how to send an updated version to an alread submitted pull request.

    Apparently all I had to do was update it.

    Anyway I need to send you an update to the SD card functions. Also hopefully the memory functions that I updated and you merged in work for you.

    Mike

  • Submitted update SD driver code.

    Speed test program that was used:

    #include <stdio.h>
    #include <sys/time.h>
    #include <stdlib.h>
    #include <propeller.h>
    #include <sys/sdcard.h>
    
    uint32_t randfill(uint32_t *, size_t);
    int  compare(uint32_t *, uint32_t *, size_t);
    
    #define PIN_SS   23
    #define PIN_MISO 20
    #define PIN_CLK  21
    #define PIN_MOSI 22
    
    uint32_t  data1[25000];
    uint32_t  data2[25000];
    struct tm tv;
    
    
    int main(int argc, char** argv)
    {
        FILE  *fh;
        uint32_t  ticks;
        time_t t;
        struct timeval x;
    
        tv.tm_year = 2022 - 1900;
        tv.tm_mon = 3;
        tv.tm_mday = 7;
        tv.tm_hour = 6;
        tv.tm_min = 0;
        tv.tm_sec = 0;
        t = mktime(&tv);
        x.tv_sec = t;
        x.tv_usec = 0;
    
        settimeofday(&x, 0);
    
        printf( " clkfreq = %d   clkmode = 0x%x\n", _clkfreq, _clkmode);
        printf( " Randfill ticks = %d\n", randfill( data1, sizeof(data1) ) );
    
        printf( " Mounting: " );
        sd_mount(0, PIN_SS, PIN_CLK, PIN_MOSI, PIN_MISO);
    
        if( (fh = fopen( "SD0:/speed2.bin", "w" )) > 0 )
        {
            ticks = getms();
            fwrite( data1, 1, sizeof(data1), fh );
            fclose( fh );
            ticks = getms() - ticks;
            printf( " Writing %u bytes at %u kB/s\n", sizeof(data1), (sizeof(data1) * 1000 / ticks + 512) >> 10 );
        } else  printf( " SD card write error!\n" );
    
        if( (fh = fopen( "SD0:/speed2.bin", "r" )) > 0 )
        {
            ticks = getms();
            fread( data2, 1, sizeof(data2), fh );
            fclose( fh );
            ticks = getms() - ticks;
            printf( " Reading %u bytes at %u kB/s\n", sizeof(data2), (sizeof(data2) * 1000 / ticks + 512) >> 10 );
            if( compare( data1, data2, sizeof(data2) ) )  printf( " Matches!  :)\n" );
            else    printf( " Mis-matches!  :(\n" );
        } else  printf( " SD card read error!\n" );
    
        while (1)
        {
            waitms(500);
        }
    }
    
    uint32_t  randfill( uint32_t *addr, size_t size )
    {
        uint32_t  ticks;
    
        size >>= 2;
        ticks = _cnt();
        do {
            *(addr++) = rand();
        } while( --size );
    
        return( _cnt() - ticks );
    }
    
    
    int  compare( uint32_t *addr1, uint32_t *addr2, size_t size )
    {
        uint32_t  pass = 1;
    
        size >>= 2;
        do {
            if( *(addr1++) != *(addr2++) )  pass = 0;
        } while( --size );
    
        return( pass );
    }
    

    Mike

  • roglohrogloh Posts: 6,316
    edited 2026-03-17 03:39

    Hi @n_ermosh finally got around to downloading LLVM on my M2 Pro based Mac to have a brief look. Not sure if you are still actively working on it or not. I downloaded the latest from your github source at ne75/p2llvm.

    Hit a few problems along the way so I thought I'd mention them in case you wanted to figure it out. It seems to be related to the use of the modulus % operator in C which is being converted to QUREM psuedo instruction presumably for later access via the Cordic.

    I've found a handful of failing files crashing LLVM Clang with an assert condition and so far the common denominator is always the use of the % operator.

    Assertion failed: ((I.atEnd() || std::next(I) == def_instr_end()) && "getVRegDef assumes a single definition or no definition"), function getVRegDef, file MachineRegisterInfo.cpp, line 404.

    This simple code below is enough to trigger it if you build with LLVM Clang using the P2 as the target. I was using the build of LLVM included in your latest tree as a submodule which I also built locally on my Mac M2 for acting as the P2 cross compiler.

    #include <stdint.h>
    uint32_t rand_seed = 2223;
    
    uint32_t rand(uint32_t min, uint32_t max)
    {
        return (rand_seed % max)+min;
    }
    

    In my digging into this problem I tried to decipher a few things using -emit-llvm on the clang command line and then passing the output file to "llc --print-after-isel" which shows this:

    # After Instruction Selection:
    # Machine code for function rand: IsSSA, TracksLiveness
    Frame Objects:
      fi#-1: size=4, align=1, fixed, at location [SP]
    Function Live Ins: $r0 in %0, $r1 in %1
    
    bb.0.entry:
      liveins: $r0, $r1
      %1:p2gpr = COPY $r1
      %0:p2gpr = COPY $r0
      %2:p2gpr = MOVri @rand_seed, 15, 0
      %3:p2gpr = RDLONGrr killed %2:p2gpr, 15, 0 :: (dereferenceable load (s32) from @rand_seed, !tbaa !3)
      %4:p2gpr = QUREM killed %3:p2gpr, %1:p2gpr
      %5:p2gpr = ADDrr %4:p2gpr(tied-def 0), %0:p2gpr, 15, 0
      $r31 = COPY %5:p2gpr
      RETA 15, 0, implicit $r31
    
    # End machine code for function rand.
    
    Assertion failed: ((I.atEnd() || std::next(I) == def_instr_end()) && "getVRegDef assumes a single definition or no definition"), function getVRegDef, file MachineRegisterInfo.cpp, line 404.
    PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace.
    Stack dump:
    0.      Program arguments: /Users/roger/Applications/p2llvm/bin/llc --print-after-isel math.bc
    1.      Running pass 'Function Pass Manager' on module 'math.bc'.
    2.      Running pass 'Live Variable Analysis' on function '@rand'
    Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
    0  llc                      0x0000000101ed7ffc llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 80
    1  llc                      0x0000000101ed85a0 PrintStackTraceSignalHandler(void*) + 28
    2  llc                      0x0000000101ed6578 llvm::sys::RunSignalHandlers() + 140
    3  llc                      0x0000000101ed9a94 SignalHandler(int) + 276
    4  libsystem_platform.dylib 0x000000018797ea24 _sigtramp + 56
    5  libsystem_pthread.dylib  0x000000018794fc28 pthread_kill + 288
    6  libsystem_c.dylib        0x000000018785dae8 abort + 180
    7  libsystem_c.dylib        0x000000018785ce44 err + 0
    8  llc                      0x0000000100b733b4 llvm::MachineRegisterInfo::getVRegDef(llvm::Register) const + 200
    9  llc                      0x000000010098a888 llvm::LiveVariables::HandleVirtRegUse(llvm::Register, llvm::MachineBasicBlock*, llvm::MachineInstr&) + 64
    10 llc                      0x000000010098d54c llvm::LiveVariables::runOnInstr(llvm::MachineInstr&, llvm::SmallVectorImpl<unsigned int>&) + 856
    11 llc                      0x000000010098d9bc llvm::LiveVariables::runOnBlock(llvm::MachineBasicBlock*, unsigned int) + 488
    12 llc                      0x000000010098e0b4 llvm::LiveVariables::runOnMachineFunction(llvm::MachineFunction&) + 428
    13 llc                      0x0000000100a7f7ac llvm::MachineFunctionPass::runOnFunction(llvm::Function&) + 456
    14 llc                      0x00000001011eecec llvm::FPPassManager::runOnFunction(llvm::Function&) + 536
    15 llc                      0x00000001011f609c llvm::FPPassManager::runOnModule(llvm::Module&) + 116
    16 llc                      0x00000001011ef5ac (anonymous namespace)::MPPassManager::runOnModule(llvm::Module&) + 672
    17 llc                      0x00000001011ef134 llvm::legacy::PassManagerImpl::run(llvm::Module&) + 288
    18 llc                      0x00000001011f64ac llvm::legacy::PassManager::run(llvm::Module&) + 36
    19 llc                      0x00000001000bf494 compileModule(char**, llvm::LLVMContext&) + 4696
    20 llc                      0x00000001000bda50 main + 1156
    21 dyld                     0x00000001875f7fd8 start + 2412
    
    

    So I'm guessing there is some problem with the QUREM "instruction" not doing the QDIV and taking the result from the GETQY or something like that before returning.

    This same problem seems to also prevent me from completing the build of your P2 C library as the file(s) it fails on include the % operator in the C code. It fails on some time functions - I identified strftime.c and localtim.c failing so far, could be more although it got to 96% so it was close! EDIT: no it's just those two files with the % operator, and when I commented out the lines with the modulus, it let the build complete and install the (now broken) libc.a file to the LLVM installation folder.

     python3 build.py --skip_llvm --skip_libp2 --install /Users/roger/Applications/p2llvm
    [  1%] Building C object time/CMakeFiles/time.dir/strftime.c.obj
    [ 15%] Built target misc
    [ 33%] Built target string
    [ 38%] Built target math
    [ 60%] Built target wchar
    [ 90%] Built target stdlib
    [ 91%] Built target stdio
    [ 91%] Building C object time/CMakeFiles/time.dir/localtim.c.obj
    [ 96%] Built target drivers
    Assertion failed: ((I.atEnd() || std::next(I) == def_instr_end()) && "getVRegDef assumes a single definition or no definition"), function getVRegDef, file MachineRegisterInfo.cpp, line 404.
    PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace, preprocessed source, and associated run script.
    Stack dump:
    0.  Program arguments: /Users/roger/Applications/p2llvm/bin/clang -I/Users/roger/Documents/Code/p2llvm/libc/include -Wall -Werror -ffunction-sections -fdata-sections -Oz -fno-exceptions --target=p2 -MD -MT time/CMakeFiles/time.dir/strftime.c.obj -MF CMakeFiles/time.dir/strftime.c.obj.d -o CMakeFiles/time.dir/strftime.c.obj -c /Users/roger/Documents/Code/p2llvm/libc/time/strftime.c
    1.  <eof> parser at end of file
    2.  Code generation
    3.  Running pass 'Function Pass Manager' on module '/Users/roger/Documents/Code/p2llvm/libc/time/strftime.c'.
    4.  Running pass 'Live Variable Analysis' on function '@strftime'
    

    I also had a minor issue of the install scripts not fully working outright due to not finding some propeller.h and propeller2.h include files and stuff about missing stdio.h for files that included that. I just copied those files over to the include folder manually and commented out these unnecessary(?) #include <stdio.h> lines and was able to get it to complete the build of the libp2 but unfortunately it doesn't work out of the box from a clean slate so to speak.

  • roglohrogloh Posts: 6,316
    edited 2026-03-17 04:05

    With respect to the prior post, I am wondering if this assert is something related to this P2 specific code below in P2ExpandPseudos.cpp that expands the Pseudo instructions where you mentioned in the comment a need to call GETQX first to flush it because I noticed that step was not happening for QUDIV which presumably works okay - although I should really try to go double check that too:

    void P2ExpandPseudos::expand_QUREM(MachineFunction &MF, MachineBasicBlock::iterator SII) {
        MachineInstr &SI = *SII;
    
        LLVM_DEBUG(errs()<<"== lower pseudo unsigned remainder\n");
        LLVM_DEBUG(SI.dump());
    
        BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(P2::QDIVrr))
                .addReg(SI.getOperand(1).getReg())
                .addReg(SI.getOperand(2).getReg())
                .addImm(P2::ALWAYS);
    
        // first call getqx so that we flush it out of the cordic. This is in case another cordic operation
        // after this calls get qx before it's done. 
        BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(P2::GETQX), SI.getOperand(0).getReg())
                .addReg(P2::QX)
                .addImm(P2::ALWAYS)
                .addImm(P2::NOEFF);
        BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(P2::GETQY), SI.getOperand(0).getReg())
                .addReg(P2::QY)
                .addImm(P2::ALWAYS)
                .addImm(P2::NOEFF);
    
        SI.eraseFromParent();
    }
    

    Here's what you do for QUDIV which is one instruction less.

    void P2ExpandPseudos::expand_QUDIV(MachineFunction &MF, MachineBasicBlock::iterator SII) {
        MachineInstr &SI = *SII;
    
        LLVM_DEBUG(errs()<<"== lower pseudo unsigned division\n");
        LLVM_DEBUG(SI.dump());
    
        BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(P2::QDIVrr))
                .addReg(SI.getOperand(1).getReg())
                .addReg(SI.getOperand(2).getReg())
                .addImm(P2::ALWAYS);
        BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(P2::GETQX), SI.getOperand(0).getReg())
                .addReg(P2::QX)
                .addImm(P2::ALWAYS)
                .addImm(P2::NOEFF);
    
        SI.eraseFromParent();
    }
    

    UPDATE: So when I removed the extra GETQX from QUREM and rebuilt LLVM it didn't crash anymore with that assert if I use the % operator in C code. :smile: So it would appear that this possible QX issue needs to be resolved in a different way somehow.

    +#if 0
         BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(P2::GETQX), SI.getOperand(0).getReg())
                 .addReg(P2::QX)
                 .addImm(P2::ALWAYS)
                 .addImm(P2::NOEFF);
    +#endif
    
  • roglohrogloh Posts: 6,316
    edited 2026-04-08 12:23

    Ugh, in my attempts to get MicroPython to compile with LLVM I'm running into a serious problem with stack frame generation and computation of parameter offset addresses in function calls with P2LLVM. This seems to be buggy and will crash MP printing code in some situations.

    E.g here's my C calling code snippet (with some puts debug stuff added)

                    puts("calling mp_print_int");
                    if (fill==' ')
                        puts("fill is a space");
                    chrs += mp_print_int(print, val, 0, base, fmt_c, flags, fill, width);
                    puts("done printing int");
    

    And here's the start of the called code which checks the "fill" argument after it is passed a space character. I found this value was not getting through correctly to the lower layers of code which is why I checked it and then printed the result here.

    STATIC int mp_print_int(const mp_print_t *print, mp_uint_t x, int sgn, int base, int base_char, int flags, char fill, int width) {
        char sign = 0;
        if (fill==' ')
            puts("fill still a space");
        else
            puts("not a space");
    
    

    Executing the code results in:

    calling mp_print_int
    fill is a space
    not a space
    ... and then the code never returns due to infinite loop lockup later due to invalid data being passed from not addressing the arguments correctly
    

    I disassembled the relevant code and it sure looks like the argument addressing is wrong.

    Caller:

        44bc: ed a1 03 f6            mov r0, r29
        44c0: ff a0 07 f5            and r0, #255 
        44c4: 20 a0 5f f2            cmps r0, #32   wcz
        44c8: 08 00 90 5d       if_nz    jmp #8
        44cc: df a1 03 f6            mov r0, r15 
        44d0: 14 bd c3 fd            calla #\puts
        44d4: db a1 03 f6            mov r0, r11    
        44d8: 01 a0 07 f1            add r0, #1 
        44dc: 06 a0 07 f5            and r0, #6 
        44e0: dd c9 03 f6            mov r20, r13
        44e4: d0 c9 83 f1            sub r20, r0
        44e8: ff ff 7f ff            augs #8388607
        44ec: f0 b7 07 f5            and r11, #496
        44f0: 0f b6 87 f1            sub r11, #15   
        44f4: 07 da 67 f7            signx r29, #7
        44f8: f8 a1 03 f6            mov r0, ptra 
        44fc: 04 a0 07 f1            add r0, #4 
        4500: d0 db 63 fc            wrlong r29, r0  <---- write "fill" parameter to SP+4
        4504: f8 a1 03 f6            mov r0, ptra  
        4508: 08 a0 07 f1            add r0, #8 
        450c: d0 a7 63 fc            wrlong r3, r0  <--- write "flags" parameter to SP+8
        4510: f8 ab 63 fc            wrlong r5, ptra  <--- write "width" parameter to SP+0
        4514: 07 b6 67 f7            signx r11, #7  
        4518: f8 a1 03 f6            mov r0, ptra
        451c: 0c a0 07 f1            add r0, #12  
        4520: d0 b7 63 fc            wrlong r11, r0  <---- write "base_char" parameter to SP+12
        4524: d4 a1 03 f6            mov r0, r4  <--- setup "*print" parameter in r0
        4528: ec a5 03 f6            mov r2, r28 <--- setup "sgn" paramter in r2
        452c: e4 a7 03 f6            mov r3, r20 <--- setup "base" parameter in r3
        4530: 10 f0 07 f1            add ptra, #16  <---- advance SP by 4 long parameters passed on the stack (16 bytes)
        4534: 38 46 c0 fd            calla #\mp_print_int <---- invoke mp_print_int function
        4538: 10 f0 87 f1            sub ptra, #16
        453c: ef a3 03 f6            mov r1, r31
        4540: e0 a1 03 f6            mov r0, r16    
        4544: 14 bd c3 fd            calla #\puts
    

    The beginning code from the called function is this:

    00004638 <mp_print_int>:         
        4638: 28 18 64 fd            setq #12
        463c: 61 a1 67 fc            wrlong r0, ptra++  <----- saves 13 registers from r0-r12
        4640: 11 f0 07 f1            add ptra, #17   <---- advances SP (for local variables?)
        4644: d3 af 03 f6            mov r7, r3   <-- preserve arg r3
        4648: d0 a9 03 f6            mov r4, r0  <---preserve arg r0
        464c: 78 02 00 ff            augs #632
        4650: f7 a1 07 f6            mov r0, #503  <--- compute first string address
        4654: 72 02 00 ff            augs #626
        4658: 77 a7 07 f6            mov r3, #375 <---- compute alternative string address
        465c: 2b b1 07 fb            rdlong r8, ptra[-21]  <---- read "fill argument" by subtracting 21 longs worth (84 bytes)  
        4660: 20 b0 5f f2            cmps r8, #32   wcz  <--- check against space character
        4664: d3 a1 03 a6       if_z     mov r0, r3   <--- matches space
        4668: d0 a1 03 56       if_nz    mov r0, r0  <--- doesn't match space
        466c: f8 ed 03 f6            mov pa, ptra   
        4670: 11 ec 87 f1            sub pa, #17  
        4674: f6 01 48 fc            wrbyte #0, pa  <--- clear local?
        4678: 14 bd c3 fd            calla #\puts  <--- print result
        467c: 00 b2 07 f6            mov r9, #0  
        4680: 2c a7 07 fb            rdlong r3, ptra[-20]
        4684: 0f a4 97 fb            tjz r2, #15 
        4688: ff ff 7f ff            augs #8388607
        468c: ff a3 5f f2            cmps r1, #511  wcz
        4690: 04 00 90 1d   if_nc_and_nz     jmp #4
    

    You can see that the SP (ptra) is being advanced by 17 for local variable use (and not divisible by 4) yet when the "fill" argument is read into r8 as a long from ptra[-21] to be checked against a space character (32) it has not really compensated for the offset correctly which is why it doesn't match a space anymore. The stack read won't be aligned to the "fill" data on the stack.

    This is not good and makes the entire function call system unreliable. Not sure if/how easy it is to fix or where to start looking. Maybe some rounding up to the next long is needed when any local variables are assigned stack space.

    EDIT: I found with some tweaks to the stack space by adding 3 more bytes to a local buffer it would align to a multiple of 4 and start to get further, so it's definitely some alignment issue. It still crashes though as there are deeper layers to also fix.

    char buf[INT_BUF_SIZE+3]; <--- I added 3 to align the stack

    Results:

    calling mp_print_int
    fill is a space
    fill still a space
    
Sign In or Register to comment.