Download Pelles C here: http://www.pellesc.se
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
</windowsSettings>
</application>
The attached sample demonstrate how it works. You can test it in both versions ANSI and WIDECHAR, just change the '#if 1' to '#if 0' to compile in ANSI mode.#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
// 1. UTF-8 (multibyte char*) -> UTF-16 (wchar_t*)
wchar_t* Utf8ToUtf16(const char* utf8_str) {
if (!utf8_str) return NULL;
// Get required buffer size in wchar_t elements (including null-terminator)
int size_needed = MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, NULL, 0);
if (size_needed == 0) return NULL;
wchar_t* wstr = (wchar_t*)malloc(size_needed * sizeof(wchar_t));
if (!wstr) return NULL;
// Perform conversion
MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, wstr, size_needed);
return wstr; // Caller must free()
}
// 2. UTF-16 (wchar_t*) -> UTF-8 (multibyte char*)
char* Utf16ToUtf8(const wchar_t* wstr) {
if (!wstr) return NULL;
// Get required buffer size in bytes (including null-terminator)
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
if (size_needed == 0) return NULL;
char* utf8_str = (char*)malloc(size_needed);
if (!utf8_str) return NULL;
// Perform conversion
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, utf8_str, size_needed, NULL, NULL);
return utf8_str; // Caller must free()
}
typedef unsigned char u8;
void pack_a(const u8 *in, u8 *out)
{
out[0] = (u8)(((in[1] & 0x0f) << 8) | in[2]);
}
void pack_b(const u8 *in, u8 *out)
{
out[0] = (u8)((((int)in[1]) & 0x0f) * 256 | in[2]);
}
pocc -Tx64-coff -std:C17 -Ze -Zx -Go -Ot -W1 -c d006_minimal.c
warning #2131: Shifting an 'unsigned char' by 8 bits is undefined.
warning #2131: Shifting an 'char' by 8 bits is undefined.
unsigned int decide(unsigned int x, unsigned int m)
{
return !((x & m) != 0) && ((x & 0x100u) == 0);
}
x & m = 0x279 & 0x002 = 0
(x & m) != 0 = 0
!((x & m) != 0) = 1
x & 0x100u = 0x279 & 0x100 = 0
(x & 0x100u) == 0 = 1
1 && 1 = 1
decide:
mov dword ptr [rsp+8],ecx
mov dword ptr [rsp+10],edx
mov eax,dword ptr [rsp+10]
test dword ptr [rsp+8],eax ; x & m
jne L_false
test dword ptr [rsp+8],100 ; x & 0x100
jne L_false
mov eax,1
ret
L_false:
mov eax,0
ret
decide:
xor eax,eax
test ecx,ecx ; x is tested against ITSELF
sete al
ret
_decide:
xor eax,eax
cmp dword ptr [esp+4],0
sete al
ret
both_const:
xor eax,eax
test ecx,102 ; 0x002 | 0x100 = 0x102 CORRECT
sete al
ret
unsigned int fused_by_hand(unsigned int x, unsigned int m)
{
return ((x & m) | (x & 0x100u)) == 0;
}
fused_by_hand:
xor eax,eax
or edx,100 ; m | 0x100
test ecx,edx
sete al
ret ; CORRECT
form emitted test verdict
----------------------------------------------------------------------
both masks compile-time constants test ecx,102 correct
first mask a parameter, second constant test ecx,ecx WRONG
first mask constant, second a parameter test ecx,ecx WRONG
both masks parameters test ecx,ecx WRONG
three conjuncts, first mask a parameter test ecx,ecx WRONG
signed int operands, otherwise identical test ecx,ecx WRONG
shared value left in first &, right in second test ecx,ecx WRONG
shared value is the right operand of both two tests correct
shared operand is a variable mask, values differ two tests correct
shared operand is a constant mask, values differ two tests correct
both masks are enumeration constants test ecx,14000 correct
single masked test, variable mask, no && test ecx,edx correct
the two tests on different variables two tests correct
fusion written by hand with | or, then test correct
|| with != 0 (the De Morgan dual) two tests correct
----------------------------------------------------------------------
decide:
xor %eax,%eax
or $0x1,%dh # sets bit 8 of edx: m | 0x100
test %ecx,%edx
sete %al
ret
if (!isFillOrder(tif, td->td_fillorder) &&
(tif->tif_flags & TIFF_NOBITREV) == 0)
TIFFReverseBits((uint8_t *)data, cc);
#define isFillOrder(tif, o) (((tif)->tif_flags & (o)) != 0)
#define TIFF_NOBITREV 0x00100U
pocc -Tx64-coff -std:C17 -c pellesc_1450_optimiser_bug.c
polink -subsystem:console -machine:x64 pellesc_1450_optimiser_bug.obj ^
crt64.lib kernel32.lib -out:noopt.exe
noopt.exe -> "All 7 cases correct." exit status 0
pocc -Tx64-coff -Ot -std:C17 -c pellesc_1450_optimiser_bug.c
polink -subsystem:console -machine:x64 pellesc_1450_optimiser_bug.obj ^
crt64.lib kernel32.lib -out:opt.exe
opt.exe -> "4 of 7 cases WRONG." exit status 1
src/pellesc_1450_optimiser_bug.c self-checking reproducer, run it twice
src/bug.c the single function, for disassembly
src/variants.c ten of the forms in the table above
src/characterise.c the same test across operand values
src/hypothesis.c demonstrates the transformation performed
src/shared_mask.c shared mask versus shared value
src/position.c which operand position matters
src/enum_masks.c enumeration constants as masks
disassembly/bug_no_optimisation_x64.asm correct
disassembly/bug_Ot_x64.asm WRONG
disassembly/bug_Ot_x86.asm WRONG
disassembly/variants_Ot_x64.asm all variants
disassembly/shared_mask_Ot_x64.asm
disassembly/position_Ot_x64.asm
disassembly/enum_masks_Ot_x64.asm
disassembly/bug_gcc_O2_control.asm GCC 13 -O2, correct
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
FindChar PROC src:DWORD,char:DWORD
eax to point the string to search for a specific character :
mov eax,DWORD PTR [esp+4]
ecx holding the character to be found :
mov ecx,DWORD PTR [esp+8]
Decrement eax so modifying eax should not interfer with the only jmp jz
dec eax
@@:
inc eax
Get a byte from eax pointing the string
mov dl,BYTE PTR [eax]
Check if it's NULL terminator
test dl,dl
If dl is NULL set dh to 1
setz dh
If ( dl XOR cl ) == 0 the we found the char we were looking for.
dl XOR cl is zero if dl == cl :
xor dl,cl
setz ch
If none of the conditions above are met then return back to the top of the loop :
or ch,dh
jz @b
retn 8
FindChar ENDP
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDefPage created in 0.065 seconds with 15 queries.