This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Compiler fault? (or am I missing something?)

Using the latest Keil C compiler (V7.00)
Executing on an Infineon XC161CJ-16F
This code appears to produce the wrong result.

At the "if" statement I expect v1 to equal v3 and for the main function to return 0 (I don't actually want to use the return value, its just a mock up piece of code). However, at the "if" statement, the value of v1 is 0x1234 and the value of v3 is 0xB1234.

Can anyone explain why?

typedef unsigned long ULONG;
typedef unsigned short USHORT;
typedef BYTE * POINTER;

POINTER v1,v2,v3,v4;
ULONG temp_ptr;
int
main(void)
{
  v1 = (POINTER)0;
  v2 = (POINTER)0x0B1234;
  v1 += (ULONG)v2;        // This assigns 0x1234 to v1, I think it should assign 0xB1234

  v3 = (POINTER)0;
  v4 = (POINTER)0x0B1234;
  temp_ptr = (ULONG)v3;
  v3 = temp_ptr+v4;      // This statement assigns 0xB1234 to v3 correctly.

  if ( v1!=v3 )
    return -1;
  else
    return 0;
}

Parents
  • So have you looked at the xhuge pointer type? Your code are just using the default pointer type for your memory model, and you haven't told us what that memory model is. The alternative is that you perform the required integer arithmetic to create the binary representation of good pointers of the pointer type you want to use. That may include a workaround for 14-bit offsets stored in 16 bits with 2 bits zeroed.

    Yes, I am using the xhuge memory model. I have 1/2 MB of RAM and 8MB of flash on board so I have to.

    What I am trying to resolve is why there is a difference between the following two statements specifically when using the Keil compiler on an XC161 target:

    char *x,*y;

    x += (unsigned long)y;
    x = x+(unsigned long)y;

    I would expect the C compiler to do exactly the same thing with both of those statements. I think both statements should give the same result, whether that result is correct, incorrect, undefined or something else doesn't really matter. I expect both results to be identical.

    What I really want to know is why they do not give the same result, specifically when using the Keil compiler on an XC161 target? i.e. what are the implementation dependencies?
    I believe that the compiler is designed to handle the segmented addressing of the XC161 correctly (i.e. as a 32 bit integer), because it works as expected in the second example. If that second example also failed then yes, I would agree that I need to handle the segmentation myself.

    Regards
    Paul

Reply
  • So have you looked at the xhuge pointer type? Your code are just using the default pointer type for your memory model, and you haven't told us what that memory model is. The alternative is that you perform the required integer arithmetic to create the binary representation of good pointers of the pointer type you want to use. That may include a workaround for 14-bit offsets stored in 16 bits with 2 bits zeroed.

    Yes, I am using the xhuge memory model. I have 1/2 MB of RAM and 8MB of flash on board so I have to.

    What I am trying to resolve is why there is a difference between the following two statements specifically when using the Keil compiler on an XC161 target:

    char *x,*y;

    x += (unsigned long)y;
    x = x+(unsigned long)y;

    I would expect the C compiler to do exactly the same thing with both of those statements. I think both statements should give the same result, whether that result is correct, incorrect, undefined or something else doesn't really matter. I expect both results to be identical.

    What I really want to know is why they do not give the same result, specifically when using the Keil compiler on an XC161 target? i.e. what are the implementation dependencies?
    I believe that the compiler is designed to handle the segmented addressing of the XC161 correctly (i.e. as a 32 bit integer), because it works as expected in the second example. If that second example also failed then yes, I would agree that I need to handle the segmentation myself.

    Regards
    Paul

Children
  • But your code does not do what you claim in this post.
    You claim that you do:

    char *x,*y;
    
    x += (unsigned long)y; // add an integer to a pointer
    x = x+(unsigned long)y; // add a pointer with an integer
    

    But the code in your original post does:

     v1 = (POINTER)0;
      v2 = (POINTER)0x0B1234;
      v1 += (ULONG)v2;        // This assigns 0x1234 to v1, I think it should assign 0xB1234
    
      v3 = (POINTER)0;
      v4 = (POINTER)0x0B1234;
      temp_ptr = (ULONG)v3;
      v3 = temp_ptr+v4;      // This statement assigns 0xB1234 to v3 correctly.
    

    Your first add does add an integer (remember your type cast)
    Your second add does add a pointer.

    As you can see, your recent post does not sum up your problem since you are there describing a completely different concept - no pointer added. Just two ways of adding an integer.

    Since a pointer and an integer may have identical size but completely different representation, it really does matter for the compiler if you perform pointer arithmetic or integer arithmetic. And not only that - unless you can find a specific document claiming exactly the limitations to Keils pointer support, you really are busy in the undefined domain.

    By the way - why do you claim to use the xhuge memory model? You don't need xhuge as a general memory model unless you want every single pointer to be able to span large data ranges. Why would there even be huge or far pointers if xhuge would be a good choice for general use?

    Select the cheapest memory model you can. Then specifically classify the pointers that needs it as the more capable pointer type.

  • Firstly, I'd like to say thanks Per Westermark, I really appreciate the time and patience you have shown trying to help me work through this problem.

    @Per Westermark

    As you can see, your recent post does not sum up your problem since you are there describing a completely different concept - no pointer added. Just two ways of adding an integer.

    Ahh crap! I was having a bad day yesterday. You are absolutely correct. I was just trying to simplify the problem and got the simplification wrong, please ignore it. The originally posted code is what I should be referring to.

    unless you can find a specific document claiming exactly the limitations to Keils pointer support, you really are busy in the undefined domain.

    This is from the Keil documentation (my emphasis):
    The C166 compiler and the L166 Linker/Locater handles all the CPU addressing modes for you. Therefore the different CPU address calculations are totally transparent to the C programmer.
    <snip>
    The huge and xhuge pointer values are representing directly physical memory addresses.

    By the way - why do you claim to use the xhuge memory model? You don't need xhuge as a general memory model unless you want every single pointer to be able to span large data ranges. Why would there even be huge or far pointers if xhuge would be a good choice for general use?

    I incorrectly thought my app was using the XLarge memory model and that the pointers by default were therefor xhuge pointers. That is not the case, my app is using the HLarge memory model and the pointers by default are therefor huge.

    I have logged the fault with KEIL support, maybe they can shed some light on it. But if you have any more suggestions, I'd like to know, thanks.

    -------------------------------------------------------------

    If I change the code line:

    typedef BYTE * POINTER; // that should have been char not BYTE by the way
    


    to

    typedef char xhuge * POINTER;
    


    then the code works as expected.

    That confirms that there is a difference in the way the compiler handles huge and xhuge pointers. However, it still does not explain why the code does not produce the same pointer value in the two different calculations.

    For example, if huge pointers were only supposed to cast the low 16 bits when cast to an integer, then both pointer calculations should have resulted in 0x1234.
    If huge pointers were supposed to cast the full 32 bit address when cast to an integer, then both pointer calculations should have returned 0xB1234.

    I'm not trying to argue that xhuge or huge should behave in a certain way (although I would like to know for sure how they are implemented to behave), I am merely trying to find out why doing the calculation one way yields a different result to doing it another way.

    Regards
    Paul