gotranspile / cxgo

Tool for transpiling C to Go.
https://gotranspile.dev
MIT License
328 stars 22 forks source link

Bug: CxGo translated go code giving different output from gcc and clang #77

Open Yeaseen opened 2 weeks ago

Yeaseen commented 2 weeks ago

Input C code (test.c)

#include <stdio.h>

int globe = 35;

int* fn1(int strd) {
    static int result; 
    result = globe;
    if (strd > 2) {
        result += 4;
    }
    result += strd;
    return &result; 
}

int main() {
    char q_q = 34;
    int p_p = 110; 
    p_p += q_q ^ p_p || globe; 
    q_q = globe ^ p_p ^ 25;
    printf("%d\n", *fn1(q_q));
    printf("%d\n", *fn1(p_p));
    int final=*fn1(q_q) += *fn1(p_p); 
    printf("Result: %d\n", final ); 
    return 0;
}

GCC+Clang Output with UBSan+Asan

Screenshot 2024-08-24 at 4 58 09 AM

Translated Go code

package main

import (
    "github.com/gotranspile/cxgo/runtime/libc"
    "github.com/gotranspile/cxgo/runtime/stdio"
    "os"
)

var globe int = 35

func fn1(strd int) *int {
    var result int
    result = globe
    if strd > 2 {
        result += 4
    }
    result += strd
    return &result
}
func main() {
    var (
        q_q int8 = 34
        p_p int  = 110
    )
    p_p += int(libc.BoolToInt(int(q_q)^p_p != 0 || globe != 0))
    q_q = int8(globe ^ p_p ^ 25)
    stdio.Printf("%d\n", *fn1(int(q_q)))
    stdio.Printf("%d\n", *fn1(p_p))
    var final int = func() int {
        p := fn1(int(q_q))
        *fn1(int(q_q)) += *fn1(p_p)
        return *p
    }()
    stdio.Printf("Result: %d\n", final)
    os.Exit(0)
}

Translated go code's output

The translated go code gives a different output from what GCC and Clang give.

Screenshot 2024-08-24 at 6 01 05 AM

Root Cause

The order of operations to calculate the final variable fails in the second part.

Modification that works fine and gives the same output as gcc and clang

I modified the Go code and it worked fine.

package main

import (
    "github.com/gotranspile/cxgo/runtime/libc"
    "github.com/gotranspile/cxgo/runtime/stdio"
    "os"
)

var globe int = 35

func fn1(strd int) *int {
    var result int
    result = globe
    if strd > 2 {
        result += 4
    }
    result += strd
    return &result
}
func main() {
    var (
        q_q int8 = 34
        p_p int  = 110
    )
    p_p += int(libc.BoolToInt(int(q_q)^p_p != 0 || globe != 0))
    q_q = int8(globe ^ p_p ^ 25)
    stdio.Printf("%d\n", *fn1(int(q_q)))
    stdio.Printf("%d\n", *fn1(p_p))
    // var final int = func() int {
    //  p := fn1(int(q_q))
    //  *fn1(int(q_q)) += *fn1(p_p)
    //  return *p
    // }()

    var final int = func() int {
        resultPointer := fn1(int(q_q))
        *resultPointer += *fn1(p_p)
        return *resultPointer
    }()

    stdio.Printf("Result: %d\n", final)
    os.Exit(0)
}

My modification which works fine

var final int = func() int {
        resultPointer := fn1(int(q_q))
        *resultPointer += *fn1(p_p)
        return *resultPointer
    }()

I think the bug is how CxGo handles these operations and produces the output go code.

Yeaseen commented 2 weeks ago

I tested this on c4go which can correctly translate this C code

dennwc commented 2 weeks ago

That's awesome! I knew there's some kind of bug that affects some cxgo-translated programs, but was not able to pin-point it so far. This must be one of them. Thanks for the report, I'll look into it when I can a few spare cycles :+1: