XC8: .asm to C ( i.e. Lowering One's Expectations)

cmartinez

Joined Jan 17, 2007
8,788
This is stupidly funny:

C:
BLE_FLAG_NOTIFY = ~BLE_FLAG_NOTIFY;
or

C:
BLE_FLAG_NOTIFY = !BLE_FLAG_NOTIFY;
resolves to:

Code:
11C9C  A438     BTFSS ble_scan_flags, 2, ACCESS
11C9E  D002     BRA 0x1CA4
11CA0  0E01     MOVLW 0x1
11CA2  D001     BRA 0x1CA6
11CA4  0E00     MOVLW 0x0
11CA6  0AFF     XORLW 0xFF
11CA8  6E1D     MOVWF potnum, ACCESS
11CAA  461D     RLNCF potnum, F, ACCESS
11CAC  461D     RLNCF potnum, F, ACCESS
11CAE  5038     MOVF ble_scan_flags, W, ACCESS
11CB0  181D     XORWF potnum, W, ACCESS
11CB2  0BFB     ANDLW 0xFB
11CB4  181D     XORWF potnum, W, ACCESS
11CB6  6E38     MOVWF ble_scan_flags, ACCESS
but:

C:
BLE_FLAG_NOTIFY ^= 1;
resolves to:

Code:
11F5E  7438     BTG ble_scan_flags, 2, ACCESS
The World of C is insane.
Unbelievable ... :oops:
 

nsaspook

Joined Aug 27, 2009
16,349
This is stupidly funny:

C:
BLE_FLAG_NOTIFY = ~BLE_FLAG_NOTIFY;
or

C:
BLE_FLAG_NOTIFY = !BLE_FLAG_NOTIFY;
resolves to:

Code:
11C9C  A438     BTFSS ble_scan_flags, 2, ACCESS
11C9E  D002     BRA 0x1CA4
11CA0  0E01     MOVLW 0x1
11CA2  D001     BRA 0x1CA6
11CA4  0E00     MOVLW 0x0
11CA6  0AFF     XORLW 0xFF
11CA8  6E1D     MOVWF potnum, ACCESS
11CAA  461D     RLNCF potnum, F, ACCESS
11CAC  461D     RLNCF potnum, F, ACCESS
11CAE  5038     MOVF ble_scan_flags, W, ACCESS
11CB0  181D     XORWF potnum, W, ACCESS
11CB2  0BFB     ANDLW 0xFB
11CB4  181D     XORWF potnum, W, ACCESS
11CB6  6E38     MOVWF ble_scan_flags, ACCESS
but:

C:
BLE_FLAG_NOTIFY ^= 1;
resolves to:

Code:
11F5E  7438     BTG ble_scan_flags, 2, ACCESS
The World of C is insane.
C on 8-bit controllers is insanely tweaky to conditions. Always try to reduce boolean operations to a bit. The compiler is not a mind-reader of the state of two run-time variables (due to sequence points).
 

WBahn

Joined Mar 31, 2012
32,933
This is stupidly funny:

C:
BLE_FLAG_NOTIFY = ~BLE_FLAG_NOTIFY;
or

C:
BLE_FLAG_NOTIFY = !BLE_FLAG_NOTIFY;
resolves to:

Code:
11C9C  A438     BTFSS ble_scan_flags, 2, ACCESS
11C9E  D002     BRA 0x1CA4
11CA0  0E01     MOVLW 0x1
11CA2  D001     BRA 0x1CA6
11CA4  0E00     MOVLW 0x0
11CA6  0AFF     XORLW 0xFF
11CA8  6E1D     MOVWF potnum, ACCESS
11CAA  461D     RLNCF potnum, F, ACCESS
11CAC  461D     RLNCF potnum, F, ACCESS
11CAE  5038     MOVF ble_scan_flags, W, ACCESS
11CB0  181D     XORWF potnum, W, ACCESS
11CB2  0BFB     ANDLW 0xFB
11CB4  181D     XORWF potnum, W, ACCESS
11CB6  6E38     MOVWF ble_scan_flags, ACCESS
but:

C:
BLE_FLAG_NOTIFY ^= 1;
resolves to:

Code:
11F5E  7438     BTG ble_scan_flags, 2, ACCESS
The World of C is insane.
Your criticism has nothing to do with C, but rather with the particular C compiler you have chosen to use.

And it's not insane at all -- it just underscores the main reason why abbreviated assignment operators are part of the language, particularly in the day of compilers running on slow machines with very limited memory.

Consider the following semantically identical statements:

x = x + 1;
x += 1;
x++;

WE, as humans, immediately see that they are equivalent and would likely translate them to assembly the same way.

But compilers are not humans, particularly simple compilers that don't put a bunch of effort into code optimization, which is the case for a lot of small target markets because developing good optimizing compilers is not a trivial task and the expense has to be justified.

So a bare-bones compiler is going to see these three statement very differently:

<id1> = <id2> + <constant>;
<id1> += <constant>;
<id1>++;

In the first case, it has to allow for the possibility that id1 and id2 are different, so the code it produces has to work for that case. In fact, this already represents an optimization that may not be incorporated into the compiler, which might see this as simply:

<id1> = <op1> + <op2>;

Where op1 and op2 can be any legal expression that must be evaluated before adding them, so it may not optimize the fact that the constant might be able to be incorporated into the instruction itself as an immediately value.

In the abbreviated-assignment case,

<id1> += <constant>;

The compiler knows that after evaluating the right hand side, it can use a code pattern that is using it to modifying a value in memory, and so it can do so more efficiently than the normal assignment. But it has to allow for possibility that the constant is any legal constant value. As before, this is already an optimized version of

<id1> += <op1>;

which the simplest of compilers would treat as covering the special case of op1 being a constant.

When the compiler sees

<id1>++;

It knows that it can simply use the most efficient means of incrementing the value in a variable, something which most processors have an instruction to do.

Having these different ways of expressing code gives the programmer the ability to guide the compiler down a path that produces more efficient code even in the simplest, most brain dead compilers.

Today, in mainstream compilers, you would likely see all three of the statements in my example compiler to exactly the same assembly code. The same would probably be true for your example. But, again, compilers targeting x86 machines have a huge market compared to compilers targeting a particular family of microcontrollers. So it's not surprising that the developers of the former have the resources and budget to build in significantly greater degrees of optimization.
 

cmartinez

Joined Jan 17, 2007
8,788
And maybe this is where LLMs could come in handy. To identify tasks that have been clumsily compiled and then optimize the code that was generated.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,337
Your criticism has nothing to do with C, but rather with the particular C compiler you have chosen to use.

And it's not insane at all -- it just underscores the main reason why abbreviated assignment operators are part of the language, particularly in the day of compilers running on slow machines with very limited memory.

Consider the following semantically identical statements:

x = x + 1;
x += 1;
x++;

WE, as humans, immediately see that they are equivalent and would likely translate them to assembly the same way.

But compilers are not humans, particularly simple compilers that don't put a bunch of effort into code optimization, which is the case for a lot of small target markets because developing good optimizing compilers is not a trivial task and the expense has to be justified.

So a bare-bones compiler is going to see these three statement very differently:

<id1> = <id2> + <constant>;
<id1> += <constant>;
<id1>++;

In the first case, it has to allow for the possibility that id1 and id2 are different, so the code it produces has to work for that case. In fact, this already represents an optimization that may not be incorporated into the compiler, which might see this as simply:

<id1> = <op1> + <op2>;

Where op1 and op2 can be any legal expression that must be evaluated before adding them, so it may not optimize the fact that the constant might be able to be incorporated into the instruction itself as an immediately value.

In the abbreviated-assignment case,

<id1> += <constant>;

The compiler knows that after evaluating the right hand side, it can use a code pattern that is using it to modifying a value in memory, and so it can do so more efficiently than the normal assignment. But it has to allow for possibility that the constant is any legal constant value. As before, this is already an optimized version of

<id1> += <op1>;

which the simplest of compilers would treat as covering the special case of op1 being a constant.

When the compiler sees

<id1>++;

It knows that it can simply use the most efficient means of incrementing the value in a variable, something which most processors have an instruction to do.

Having these different ways of expressing code gives the programmer the ability to guide the compiler down a path that produces more efficient code even in the simplest, most brain dead compilers.

Today, in mainstream compilers, you would likely see all three of the statements in my example compiler to exactly the same assembly code. The same would probably be true for your example. But, again, compilers targeting x86 machines have a huge market compared to compilers targeting a particular family of microcontrollers. So it's not surprising that the developers of the former have the resources and budget to build in significantly greater degrees of optimization.
Got it.

According to @nsaspook, there's not enough business to offer a good assembler, and, according to you, there's also not enough business to offer a good compiler.

I should have run off with that rock and roll band when I had the chance.
 

WBahn

Joined Mar 31, 2012
32,933
And maybe this is where LLMs could come in handy. To identify tasks that have been clumsily compiled and then optimize the code that was generated.
And do YOU want to fly in the airplane whose flight control computers have had their clumsily compiled code optimized in this way?

Remember, these LLMs simply have NO comprehension of the meaning of what they are generating, meaning that they have no concern regarding whether their output is actually correct, just whether it fits the general patterns that they see most often.
 

cmartinez

Joined Jan 17, 2007
8,788
Got it.

According to @nsaspook, there's not enough business to offer a good assembler, and, according to you, there's also not enough business to offer a good compiler.

I should have run off with that rock and roll band when I had the chance.
I bet that the deal was off when you learned you were required to wear a mullet ...
 

cmartinez

Joined Jan 17, 2007
8,788
And do YOU want to fly in the airplane whose flight control computers have had their clumsily compiled code optimized in this way?

Remember, these LLMs simply have NO comprehension of the meaning of what they are generating, meaning that they have no concern regarding whether their output is actually correct, just whether it fits the general patterns that they see most often.
Oh, that much is quite clear to me already.

But I bet that LLMs could be useful for small and precisely cut tasks such as asking it to optimize a few lines of basic code and test the results in all possible scenarios.

Then again, I'm unaware of how much work one would have to pour into the LLM's interface to accomplish just that.
 

WBahn

Joined Mar 31, 2012
32,933
Oh, that much is quite clear to me already.

But I bet that LLMs could be useful for small and precisely cut tasks such as asking it to optimize a few lines of basic code and test the results in all possible scenarios.

Then again, I'm unaware of how much work one would have to pour into the LLM's interface to accomplish just that.
A few problems with this approach, both technical and human-factors.

If you are relying on the human responsible for the code to verify that their AI-generated code is correct, you run right up against the natural human tendency, including by humans that should damn-well know better (and most don't) that anything produced by a computer must be correct. The fact that they are using an AI tool in the first place is a bad sign that they are either insufficiently-skilled or sufficiently-lazy as to bias them toward just accepting what is spewed forth.

If you are relying on the computer verifying the correctness before it spews forth, you have the problem that testing the results in all possible scenarios doesn't scale. Consider a simple bitwise operation between two 32-bit operands (such as XORing two variables together or performing a simple masking operation). If you could evaluate one billion possible scenarios every second, you could validate this piece of code in just under 585 years. Code validation requires testing a tiny fraction of the possible use cases and generally relies on carefully crafting test cases based on the big-picture semantics of the program.
 

nsaspook

Joined Aug 27, 2009
16,349
Got it.

According to @nsaspook, there's not enough business to offer a good assembler, and, according to you, there's also not enough business to offer a good compiler.

I should have run off with that rock and roll band when I had the chance.
Are you using XC8 non-free (free is up to level 2) optimization options? If not get a trial.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,337
So a bare-bones compiler is going to see these three statement very differently:

<id1> = <id2> + <constant>;
<id1> += <constant>;
<id1>++;
Well, it's not stupid all the time (just sometimes, and at random):

C:
324:                    ble_rx_state++;                 //scan to end of line
11E82  2BE2     INCF ble_rx_state, F, BANKED


325:                    ble_rx_state = ble_rx_state + 1;
11E84  29E2     INCF ble_rx_state, W, BANKED
Are you using XC8 non-free (free is up to level 2) optimization options? If not get a trial.
You just keep making the point for me: that C is the wrong tool for what I do.

1. Assembly optimizations are free. I don't have to pay extra (one time or monthly) to produce better code.

2. This is the Microsoft Business Plan: here's a crappy OS for $99. And for $199 per year, we'll let you borrow some software that will make it better. And later: you don't need to buy an OS, just rent it from us for a small monthly charge.

Your going to say: why should Microchip supply me with an optimized compiler at no charge?

Because, the code I write with it is going to be shipped inside 10s of thousands of their Microchips every year (yes, I understand I am small beans to them). This is worth more to them than the $50/mo. charge for the optimizer. If my frustration with them continues, they may wind up losing this small portion of their business entirely -- not that it would matter.

Microchip used to act like a development partner. They now act like a marketing organization.
 

nsaspook

Joined Aug 27, 2009
16,349
Well, it's not stupid all the time (just sometimes, and at random):

C:
324:                    ble_rx_state++;                 //scan to end of line
11E82  2BE2     INCF ble_rx_state, F, BANKED


325:                    ble_rx_state = ble_rx_state + 1;
11E84  29E2     INCF ble_rx_state, W, BANKED


You just keep making the point for me: that C is the wrong tool for what I do.

1. Assembly optimizations are free. I don't have to pay extra (one time or monthly) to produce better code.

2. This is the Microsoft Business Plan: here's a crappy OS for $99. And for $199 per year, we'll let you borrow some software that will make it better. And later: you don't need to buy an OS, just rent it from us for a small monthly charge.

Your going to say: why should Microchip supply me with an optimized compiler at no charge?

Because, the code I write with it is going to be shipped inside 10s of thousands of their Microchips every year (yes, I understand I am small beans to them). This is worth more to them than the $50/mo. charge for the optimizer. If my frustration with them continues, they may wind up losing this small portion of their business entirely -- not that it would matter.

Microchip used to act like a development partner. They now act like a marketing organization.
Socialist. ;)

If you're not up to the challenge (making software design patterns not directly directly to machine instructions that compile to efficient code) of HLL low-level embedded software design then don't use C. Switch hardware and restart the task of learning that hardware's assembly quirks/gotchas or stick with C on a new platform with much the same compiler issues.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,337
If you're not up to the challenge...
No one is.

Except for trivial snippets, C is incapable of producing code, either size or speed, close to that which a good .asm programmer can write.

My WAG at this point: the best C programmer (with time heavily invested in researching and memorizing the best writing approach and code structure) is probable 2:1 worse than the best .asm programmer.

And it only gets worse from there.
 

ApacheKid

Joined Jan 12, 2015
1,762
This is stupidly funny:

C:
BLE_FLAG_NOTIFY = ~BLE_FLAG_NOTIFY;
or

C:
BLE_FLAG_NOTIFY = !BLE_FLAG_NOTIFY;
resolves to:

Code:
11C9C  A438     BTFSS ble_scan_flags, 2, ACCESS
11C9E  D002     BRA 0x1CA4
11CA0  0E01     MOVLW 0x1
11CA2  D001     BRA 0x1CA6
11CA4  0E00     MOVLW 0x0
11CA6  0AFF     XORLW 0xFF
11CA8  6E1D     MOVWF potnum, ACCESS
11CAA  461D     RLNCF potnum, F, ACCESS
11CAC  461D     RLNCF potnum, F, ACCESS
11CAE  5038     MOVF ble_scan_flags, W, ACCESS
11CB0  181D     XORWF potnum, W, ACCESS
11CB2  0BFB     ANDLW 0xFB
11CB4  181D     XORWF potnum, W, ACCESS
11CB6  6E38     MOVWF ble_scan_flags, ACCESS
but:

C:
BLE_FLAG_NOTIFY ^= 1;
resolves to:

Code:
11F5E  7438     BTG ble_scan_flags, 2, ACCESS
The World of C is insane.
This is nothing whatsoever to do with 'C' though I have my somewhat critical views on the C language this is 100% about the implementation used by the compiler engineer for that compiler.
 

ApacheKid

Joined Jan 12, 2015
1,762
Or just give me a freakin' decent assembler so that I can write good code to start with.
Grow a pair, code it in machine code. I've not dug into this before but I also suspect assembly code can be parsed using regular expressions, that is to say assembly language is regular language and I suspect it could be if one designed it to be.

In other words you could possibly write a straightforward substitution function that translates assembly mnemonics to machine code. So why not design a minimal "assembly" language that makes life easier and use that?

What processor is this anyway? can you show me its instruction set?
 

nsaspook

Joined Aug 27, 2009
16,349
No one is.

Except for trivial snippets, C is incapable of producing code, either size or speed, close to that which a good .asm programmer can write.

My WAG at this point: the best C programmer (with time heavily invested in researching and memorizing the best writing approach and code structure) is probable 2:1 worse than the best .asm programmer.

And it only gets worse from there.
Mainly true but but also mainly irrelevant in the vast majority of cases.

If that were the only criteria for success in a programming project it would be a critical point against coding in C but it's not in the vast majority of cases. Good C programmers (that understand the limits of HLL translation to machine code) switch (themselves or use the resources of an expert) to ASM when size or speed is needed, it's not the default , by design it's compromise that makes other elements of software engineering much more important in the total engineering objective.

This compromise has given us large C embedded projects like the Linux kernel and many of the tools used today. The choice is up to the developer. Perfect is the enemy of good
 
Last edited:

Thread Starter

joeyd999

Joined Jun 6, 2011
6,337
Mainly true but but also mainly irrelevant in the vast majority of cases.

If that were the only criteria for success in a programming project it would be a critical point against coding in C but it's not in the vast majority of cases. Good C programmers (that understand the limits of HLL translation to machine code) switch (themselves or use the resources of an expert) to ASM when size or speed is needed, it's not the default , by design it's compromise that makes other elements of software engineering much more important in the total engineering objective.
You keep confusing me with the "vast majority".

I'm starting to feel insulted.
 
Top