Skip to content

Commit

Permalink
feat: do not return directly string[] from compilers, use `X86Compi…
Browse files Browse the repository at this point in the history
…leInstructionOutput` instead
  • Loading branch information
Mati365 committed Dec 31, 2023
1 parent 039fa9c commit 92eb5e7
Show file tree
Hide file tree
Showing 30 changed files with 244 additions and 156 deletions.
19 changes: 14 additions & 5 deletions packages/compiler-pico-c/src/arch/x86/backend/X86Allocator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import type { IRBlockIterator } from '../../../frontend/ir/iterator';
import { X86StackFrame } from './X86StackFrame';
import { X86BasicRegAllocator } from './reg-allocator';
import { genInstruction } from '../asm-utils';
import { X86CompileInstructionOutput } from './compilers';

export type X86StackFrameContentFn = () => { asm: string[] };
export type X86StackFrameContentFn = () => X86CompileInstructionOutput;

export class X86Allocator {
private _stackFrame: X86StackFrame;
Expand Down Expand Up @@ -35,7 +36,9 @@ export class X86Allocator {
/**
* Allocates whole function declaration IR code and injects code into it
*/
allocStackFrameInstructions(contentFn: X86StackFrameContentFn): string[] {
allocStackFrameInstructions(
contentFn: X86StackFrameContentFn,
): X86CompileInstructionOutput {
const { config } = this;
const { arch } = config;

Expand All @@ -45,7 +48,10 @@ export class X86Allocator {
case CCompilerArch.X86_16: {
const content = contentFn();

return [...this.genFnTopStackFrame(), ...content.asm];
return X86CompileInstructionOutput.ofInstructions([
this.genFnTopStackFrame(),
content,
]);
}

default:
Expand All @@ -65,10 +71,13 @@ export class X86Allocator {
asm.push(genInstruction('sub', 'sp', allocBytes));
}

return asm;
return X86CompileInstructionOutput.ofInstructions(asm);
}

genFnBottomStackFrame() {
return [genInstruction('mov', 'sp', 'bp'), genInstruction('pop', 'bp')];
return X86CompileInstructionOutput.ofInstructions([
genInstruction('mov', 'sp', 'bp'),
genInstruction('pop', 'bp'),
]);
}
}
27 changes: 19 additions & 8 deletions packages/compiler-pico-c/src/arch/x86/backend/X86ArchBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import {
import { X86Allocator } from './X86Allocator';
import { X86BackendCompilerContext } from '../constants/types';

import { compileDataSegment, compileInstructionsBlock } from './compilers';
import {
X86CompileInstructionOutput,
compileDataSegment,
compileInstructionsBlock,
} from './compilers';

import { getCompilerArchDescriptor } from '../../../arch';
import { X86LabelsResolver } from './X86LabelsResolver';

Expand All @@ -21,14 +26,20 @@ export class X86ArchBackend extends CAbstractArchBackend {

compileIR({ segments }: IRScopeGeneratorResult): CBackendCompilerResult {
const asm: string[] = [`cpu ${X86ArchBackend.cpu}`];
const { labelsResolver, asm: dataAsm } = compileDataSegment({
const dataOutput = compileDataSegment({
arch: X86ArchBackend.arch,
segment: segments.data,
});

const functionsOutput = this.compileIRFunctions(
dataOutput.labelsResolver,
segments.code,
);

asm.push(
...this.compileIRFunctions(labelsResolver, segments.code),
...dataAsm,
...functionsOutput.asm,
...functionsOutput.data,
...dataOutput.output.asm,
);

return {
Expand All @@ -39,8 +50,8 @@ export class X86ArchBackend extends CAbstractArchBackend {
private compileIRFunctions(
labelsResolver: X86LabelsResolver,
codeSegment: IRFlatCodeSegmentBuilderResult,
): string[] {
const asm: string[] = [];
) {
const output = new X86CompileInstructionOutput();

for (const [, fn] of Object.entries(codeSegment.functions)) {
const iterator = IRBlockIterator.of(fn.block.instructions);
Expand All @@ -55,9 +66,9 @@ export class X86ArchBackend extends CAbstractArchBackend {
labelsResolver,
};

asm.push(...compileInstructionsBlock({ context }));
output.appendGroup(compileInstructionsBlock({ context }));
}

return asm;
return output;
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { X86CompileInstructionOutput } from '../compilers';

import {
IRCallInstruction,
IRFnDeclInstruction,
Expand All @@ -23,7 +25,7 @@ export type X86FnRetCompilerAttrs = X86FnBasicCompilerAttrs & {
};

export interface X86ConventionalFnCaller {
compileIRFnCall(attrs: X86FnCallerCompilerAttrs): string[];
compileIRFnRet(attrs: X86FnRetCompilerAttrs): string[];
compileIRFnCall(attrs: X86FnCallerCompilerAttrs): X86CompileInstructionOutput;
compileIRFnRet(attrs: X86FnRetCompilerAttrs): X86CompileInstructionOutput;
allocIRFnDefArgs(attrs: X86FnBasicCompilerAttrs): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import { getTypeOffsetByteSize } from 'frontend/ir/utils';
import { getX86RegByteSize } from '../../constants/regs';
import { genInstruction, withInlineComment } from '../../asm-utils';

import { compileMemcpy, compileStackMemcpy } from '../compilers/shared';
import {
X86CompileInstructionOutput,
compileMemcpy,
compileStackMemcpy,
} from '../compilers/shared';
import { isRegOwnership } from '../reg-allocator/utils';
import { IRArgDynamicResolverType } from '../reg-allocator';

Expand All @@ -36,13 +40,13 @@ export class X86StdcallFnCaller implements X86ConventionalFnCaller {
declInstruction,
context,
callerInstruction,
}: X86FnCallerCompilerAttrs): string[] {
}: X86FnCallerCompilerAttrs) {
const { allocator } = context;
const { regs } = allocator;

const stack = this.getContextStackInfo(allocator);
const totalNonRVOArgs = callerInstruction.args.length;
const asm: string[] = [];
const output = new X86CompileInstructionOutput();

// preserve already allocated regs on stack
regs.ownership.releaseNotUsedLaterRegs(
Expand All @@ -68,8 +72,8 @@ export class X86StdcallFnCaller implements X86ConventionalFnCaller {
throw new CBackendError(CBackendErrorCode.NON_CALLABLE_STRUCT_ARG);
}

asm.push(
...compileStackMemcpy({
output.appendGroup(
compileStackMemcpy({
allocator,
arg,
ownership: regs.ownership,
Expand All @@ -83,15 +87,18 @@ export class X86StdcallFnCaller implements X86ConventionalFnCaller {
size: stack.size,
});

asm.push(...resolvedArg.asm, genInstruction('push', resolvedArg.value));
output.appendInstructions(
...resolvedArg.asm,
genInstruction('push', resolvedArg.value),
);

if (resolvedArg.type === IRArgDynamicResolverType.REG) {
allocator.regs.releaseRegs([resolvedArg.value]);
}
}
}

asm.push(genInstruction('call', address));
output.appendInstructions(genInstruction('call', address));

// restore result from register (AX is already loaded in `compileIRFnRet`)
if (
Expand All @@ -113,10 +120,15 @@ export class X86StdcallFnCaller implements X86ConventionalFnCaller {
totalNonRVOArgs - declInstruction.getArgsWithRVO().length;

if (argsCountDelta) {
asm.push(genInstruction('add', stack.reg, argsCountDelta * stack.size));
output.appendInstructions(
genInstruction('add', stack.reg, argsCountDelta * stack.size),
);
}

return [...preservedRegs.preserve, ...asm, ...preservedRegs.restore];
return new X86CompileInstructionOutput(
[...preservedRegs.preserve, ...output.asm, ...preservedRegs.restore],
output.data,
);
}

/**
Expand Down Expand Up @@ -158,18 +170,18 @@ export class X86StdcallFnCaller implements X86ConventionalFnCaller {
context,
declInstruction,
retInstruction,
}: X86FnRetCompilerAttrs): string[] {
}: X86FnRetCompilerAttrs) {
const { allocator } = context;

const stack = this.getContextStackInfo(allocator);
const totalArgs = declInstruction.getArgsWithRVO().length;
const asm: string[] = [];
const output = new X86CompileInstructionOutput();

if (!declInstruction.isVoid() && retInstruction.value) {
if (declInstruction.hasRVO()) {
// copy structure A to B
asm.push(
...compileMemcpy({
output.appendGroup(
compileMemcpy({
context,
outputVar: declInstruction.getRVOOutputVar(),
inputVar: retInstruction.value as IRVariable,
Expand All @@ -194,7 +206,9 @@ export class X86StdcallFnCaller implements X86ConventionalFnCaller {
getX86RegByteSize(usedOwnership.reg) ===
1
) {
asm.push(genInstruction('movzx', returnReg, usedOwnership.reg));
output.appendInstructions(
genInstruction('movzx', returnReg, usedOwnership.reg),
);
} else {
// handle case when we call `return 2`
const retResolvedArg = allocator.regs.tryResolveIRArgAsReg({
Expand All @@ -203,21 +217,21 @@ export class X86StdcallFnCaller implements X86ConventionalFnCaller {
allowedRegs: [returnReg],
});

asm.push(...retResolvedArg.asm);
output.appendInstructions(...retResolvedArg.asm);
allocator.regs.releaseRegs([retResolvedArg.value]);
}
}
}

asm.push(...allocator.genFnBottomStackFrame());
output.appendGroup(allocator.genFnBottomStackFrame());

if (totalArgs) {
asm.push(genInstruction('ret', totalArgs * stack.size));
output.appendInstructions(genInstruction('ret', totalArgs * stack.size));
} else {
asm.push(genInstruction('ret'));
output.appendInstructions(genInstruction('ret'));
}

return asm;
return output;
}

private getContextStackInfo(allocator: X86Allocator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import { X86CompilerInstructionFnAttrs } from '../../../constants/types';
import { compileAsmClobbers } from './compileAsmClobbers';
import { compileAsmInputs } from './compileAsmInputs';
import { compileAsmOutputs } from './compileAsmOutputs';
import { X86CompileInstructionOutput } from '../shared';

type AsmInstructionCompilerAttrs =
X86CompilerInstructionFnAttrs<IRAsmInstruction>;

export function compileAsmInstruction({
context,
instruction,
}: AsmInstructionCompilerAttrs): string[] {
}: AsmInstructionCompilerAttrs) {
const { allocator } = context;
const { inputOperands, outputOperands, clobberOperands } = instruction;
const asm: string[] = [];

const inputResult = compileAsmInputs({
interpolatedExpression: trimLines(instruction.expression),
Expand All @@ -34,19 +34,17 @@ export function compileAsmInstruction({
clobberOperands,
});

asm.push(
allocator.regs.releaseRegs([
...inputResult.allocatedRegs,
...outputResult.allocatedRegs,
]);

return new X86CompileInstructionOutput([
...inputResult.asm,
...outputResult.asm.pre,
...clobbersResult.pre,
outputResult.interpolatedExpression,
...clobbersResult.post,
...outputResult.asm.post,
);

allocator.regs.releaseRegs([
...inputResult.allocatedRegs,
...outputResult.allocatedRegs,
]);

return asm;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { IRCallInstruction } from 'frontend/ir/instructions';
import { X86CompilerInstructionFnAttrs } from 'arch/x86/constants/types';
import { genInstruction } from 'arch/x86/asm-utils';
import { isIRVariable } from 'frontend/ir/variables';
import { X86CompileInstructionOutput } from '../shared';

type BuiltinAllocaCallAttrs = X86CompilerInstructionFnAttrs<IRCallInstruction>;

Expand Down Expand Up @@ -37,10 +38,10 @@ export const compileBuiltinAlloca = ({
regs.ownership.dropOwnership(sizeArg.name);
}

return [
return X86CompileInstructionOutput.ofInstructions([
...outputReg.asm,
...size.asm,
genInstruction('sub', 'sp', size.value),
genInstruction('mov', outputReg.value, 'sp'),
];
]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CBackendError, CBackendErrorCode } from 'backend/errors/CBackendError';

import { compileBuiltinVaArg, compileBuiltinVaStart } from './va';
import { compileBuiltinAlloca } from './compileBuiltinAlloca';
import { X86CompileInstructionOutput } from '../shared';

type BuiltinFnCallAttrs = X86CompilerInstructionFnAttrs<IRCallInstruction>;

Expand All @@ -23,7 +24,7 @@ export const compileBuiltinCallFn = (attrs: BuiltinFnCallAttrs) => {
return compileBuiltinVaArg(attrs);

case withBuiltinPrefix('va_end'):
return [];
return X86CompileInstructionOutput.ofInstructions([]);

default:
throw new CBackendError(CBackendErrorCode.UNKNOWN_BUILTIN_FUNCTION, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { IRCallInstruction } from 'frontend/ir/instructions';
import { X86CompilerInstructionFnAttrs } from 'arch/x86/constants/types';
import { genComment, genInstruction, genMemAddress } from 'arch/x86/asm-utils';
import { isIRConstant, isIRVariable } from 'frontend/ir/variables';
import { X86CompileInstructionOutput } from '../../shared';

type BuiltinVaArgCallAttrs = X86CompilerInstructionFnAttrs<IRCallInstruction>;

Expand Down Expand Up @@ -34,7 +35,7 @@ export const compileBuiltinVaArg = ({
});

if (!isIRConstant(sizeArg)) {
return [];
return X86CompileInstructionOutput.ofInstructions([]);
}

const loadedValReg = regs.requestReg({ size: sizeArg.constant });
Expand All @@ -49,7 +50,7 @@ export const compileBuiltinVaArg = ({

regs.releaseRegs([vaPtrReg.value, loadedValReg.value]);

return [
return X86CompileInstructionOutput.ofInstructions([
...vaListPtr.asm,
...vaPtrReg.asm,
...loadedValReg.asm,
Expand Down Expand Up @@ -78,5 +79,5 @@ export const compileBuiltinVaArg = ({
vaPtrReg.value,
),
genComment('VA arg getter - end'),
];
]);
};
Loading

0 comments on commit 92eb5e7

Please sign in to comment.