Skip to content

Performance regression in April 2026 release for 8086 DOS target #1610

Description

@ggeorgovassilis

Performance Regression: Less optimal register allocation and branch inversion on 8086 (-ox -ok)

Description

With the Open Watcom Beta release (April 2026), testing an 8086 C++ code base targeting DOS revealed a performance regression compared to the March 2026 release. Through disassembly diffing, I traced the regression strictly to how the clampi logic (a small inline function) was lowered to assembly inside a complex translation unit (for reference: Object3D::addDirtyRect), when compiled with maximal optimisations -bt=dos -0 -ml -wx -oa -oe -oh -oi+ -ok -ol -os -ox -oz -s -zm -zq. This issue is specific to the space-optimisation flag -os.

Detailed Observation

The regression takes the form of replacing fast arithmetic (e.g. dec accompanied by optimal register-to-register moves) with lea effective-address instructions, shifting which register holds values in branches, and generating flipped jg/jle condition blocks that result in slightly worse throughput on an actual 8086 processor.

C++ Source extract:

static inline int clampi(int v, int lo, int hi) {
    if (v < lo) return lo;
    if (v > hi) return hi;
    return v;
}

void Object3D::addDirtyRect(const RenderContext &ctx, short minX, short minY,
                            short maxX, short maxY) {
    const short w = (short)ctx.screenW;
    const short h = (short)ctx.screenH;
    
    if (maxX < 0 || minX >= w || maxY < 0 || minY >= h) return;

    minX = clampi(minX, 0, w - 1);
    minY = clampi(minY, 0, h - 1);
    maxX = clampi(maxX, 0, w - 1);
    maxY = clampi(maxY, 0, h - 1);
    // ...
}

March Release Generated Code (Good)

        mov             dx,ax
        dec             dx                   ; dx = w - 1 (2 clocks)
        test            bx,bx
        jge             L$54
        xor             bx,bx
        jmp             L$55
L$54:
        cmp             bx,dx
        jle             L$55
        mov             bx,dx
L$55:

April Release Generated Code (Regressed)

        lea             ax,-1[di]            ; ax = w - 1 (slower LEA on 8086)
        test            bx,bx
        jge             L$54
        xor             ax,ax
        jmp             L$55
L$54:
        cmp             bx,ax
        jg              L$55
        mov             ax,bx
L$55:

Additionally, more values spill from registers requiring reload across similar clamping calls further down.

Steps to Reproduce

Unfortunately, the codebase which exhibits this behaviour is complex and I wasn't able to extract a reliable test case. Because global register flow optimisations (-ok, -ox) are stateful with respect to the translation unit, extracting just the clampi function into a trivial test.cpp causes the compiler to magically perfectly allocate registers due to the lack of surrounding register pressure.

Expected Behaviour

The new release should ideally preserve the optimal instruction selection/register mapping of the March compile, avoiding slower lea instructions compared to simple register arithmetic (dec).

Closing notes

Please note that the compiler "traditionally" produces faster code with -os than -ot for the 8086, which is the motivation behind this report. I casually observed that the April release produces faster output for -ot than the March release, though for both March and April -os is faster than -ot, so if speed is an objective, the April release is for my code base a regression as there is no workaround.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C++C++ compilerCGCode generatorbug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions