30 years or so after my first visit to the island, my Ios page has finally been updated with some actual walks. Is Ios a good island for walking? Absolutely not! But it has other qualities…
Ioannina page
I have made a small page about Ioannina. There is at least one particularly wonderful walk/stroll you can do in this beautiful town.
Ioannina
New pictures, this time from beautiful Ioannina.
Symi Updates
The Symi page has updates for most of the walks from last year plus two new short walks.
Symi Again
More pictures from Symi at https://www.islandwalking.com/piwigo/index.php?/category/167.
LLVM Exercise VII
So why does the code produced in the last exercise contain the labels .LBB0_2 and .LBB0_3 but no .LBB0_1?
Well that is because I have added a code generation pass that deletes useless jumps like:
JNC .LBB0_1
.LBB0_1:
Code snippets:
void HP41MCODEPassConfig::addPreEmitPass() {
addPass(new RemoveUselessJMP()); }
...
bool RemoveUselessJMP::runOnMachineBasicBlock(
MachineBasicBlock &MBB, MachineBasicBlock &MBBN) {
bool Modified = false;
MachineBasicBlock::iterator I = MBB.end();
if (I != MBB.begin())
I--;
else
return Modified;
if (I->getOpcode() == HP41MCODE::JNC &&
I->getOperand(0).getMBB() == &MBBN) {
MBB.erase(I);
Modified = true;
}
return Modified;
}
bool RemoveUselessJMP::runOnMachineFunction(MachineFunction &MF) {
bool Modified = false;
MachineFunction::iterator FJ = MF.begin();
if (FJ != MF.end())
FJ++;
if (FJ == MF.end())
return Modified;
for (MachineFunction::iterator FI = MF.begin(),
FE = MF.end(); FJ != FE; ++FI, ++FJ)
Modified |= runOnMachineBasicBlock(*FI, *FJ);
return Modified;
}
LLVM Exercise VI
It is time to bring in some conditionals. Adding an “if” to our friend foo():
int foo() {
int retval1 = 0x0AB;
int retval2 = 0;
if (retval2)
return retval2;
return retval1;
}
We need to implement instructions for both conditional and unconditional branches (i.e jumps). An unconditional branch is just like this (assuming no carry flag is set):
...
def brtarget : Operand;
...
let isBarrier=1, isBranch=1, isTerminator=1 in
{
def JNC : HP41MCODEInst<0x00B, (outs), (ins brtarget:$addr),
"JNC $addr", [(br bb:$addr)]>;
}
We will use custom lowered pseudo instructions as an easy solution. This is a start:
class HP41MCODEPseudo pattern> : HP41MCODEInst<0, outs, ins,
asmstr, pattern> {
let isCodeGenOnly = 1;
let isPseudo = 1;
}
...
SDT_HP41MCODEBRCC : SDTypeProfile<0, 4, [SDTCisVT<0,
OtherVT>]>;
def HP41MCODEBRCC : SDNode<"HP41MCODEISD::BRCC",
SDT_HP41MCODEBRCC, [SDNPHasChain, SDNPInGlue]>;
...
let isBranch=1, isTerminator=1 in
{
def BRCC : HP41MCODEPseudo< (outs), (ins brtarget:$addr,
RC:$lhs, i32imm:$rhs, i32imm:$cond),
"#BRCC $addr $lhs $rhs $cond",
[(HP41MCODEBRCC bb:$addr, RC:$lhs, (i32 imm:$rhs),
(i32 imm:$cond))]>;
}
Note that this instruction will only match the case we have in this example. Different cases should use different instructions. More code snippets:
...
BRCC, // Branch to dest on condition
...
SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const;
...
setOperationAction(ISD::BR_CC, MVT::i32, Custom);
...
case HP41MCODEISD::BRCC: return "HP41MCODEISD::BRCC";
...
SDValue HP41MCODETargetLowering::LowerBR_CC(SDValue Op,
SelectionDAG &DAG) const {
SDValue Chain = Op.getOperand(0);
ISD::CondCode CC = cast(Op.getOperand(1))->get();
SDValue LHS = Op.getOperand(2);
SDValue RHS = Op.getOperand(3);
SDValue Dest = Op.getOperand(4);
SDLoc dl(Op);
return DAG.getNode(HP41MCODEISD::BRCC, dl, MVT::Other,
Chain, Dest, LHS, RHS,
DAG.getConstant(CC, dl, MVT::i32));
}
...
case ISD::BR_CC:
return LowerBR_CC(Op, DAG);
So for this example we only need to produce machine instructions that compares the C register to 0 and then branches if it is equal. This must be done in a post-RA pass:
bool HP41MCODEInstrInfo::expandPostRAPseudo(
MachineInstr &MI) const {
MachineBasicBlock &MBB = *MI.getParent();
MachineFunction &MF = *MBB.getParent();
const HP41MCODEInstrInfo &TII = *static_cast(
MF.getSubtarget().getInstrInfo());
DebugLoc dl = MBB.findDebugLoc(MI);
switch (MI.getOpcode()) {
case HP41MCODE::BRCC: {
assert(MI.getOperand(1).getReg() == HP41MCODE::C &&
"Not implemented!");
assert(MI.getOperand(2).getImm() == 0 &&
"Not implemented!");
assert(MI.getOperand(3).getImm() == ISD::SETEQ &&
"Not implemented!");
BuildMI(MBB, MI, dl, TII.get(HP41MCODE::qCneq0ALL));
BuildMI(MBB, MI, dl,
TII.get(HP41MCODE::JNC)).addMBB(
MI.getOperand(0).getMBB());
MI.eraseFromParent();
return true;
}
}
return false;
}
This is the machine instruction used:
def qCneq0ALL : HP41MCODEInst<0x2EE, (outs), (ins),
"?C!=0 ALL", []>;
And we get:
.file "hello.c"
.text
.globl foo ! -- Begin function foo
.type foo,@function
foo: ! @foo
! %bb.0: ! %entry
LDI S&X HEX: 0AB
WRIT 2
C=0 ALL
WRIT 1
READ 1
?C!=0 ALL
JNC .LBB0_2
! %bb.1: ! %if.then
READ 1
WRIT 3
JNC .LBB0_3
.LBB0_2: ! %if.end
READ 2
WRIT 3
.LBB0_3: ! %return
READ 3
RTN
.Lfunc_end0:
.size foo, .Lfunc_end0-foo
! -- End function
.ident "clang version 20.0.0git (https://github.com/llvm/llvm-project.git ea1dfd50bfdfbd75969fd7653bc71c81f2a2350f)"
.section ".note.GNU-stack"
.addrsig
LLVM Exercise V
Let us add some local variables:
int foo() {
int retval = 0x0AB;
int dummy = 0;
return retval;
}
This requires adding some information about accessing memory. I will just postulate that I have memory available from address 1 growing upwards. As anyone that really knows the HP-41 understands, this is a very bad idea. However we are still just showing some basic LLVM principles, not producing actually useful Nut code. I am however excluding the possibility of generating READ 0, as that instruction does not even exist.
Some code snippets:
// Addressing modes.
def ADDR : ComplexPattern<iPTR, 2, "SelectADDR",
[frameindex], []>;
// Address operands
def MEM : Operand {
let MIOperandInfo = (ops RC, i16imm);
let PrintMethod = "printMemOperand";
let DecoderMethod = "decodeMemOperand";
}
...
let mayLoad=1 in {
def READ : HP41MCODEInst<0x078, (outs RC:$r),
(ins MEM:$addr), "READ $addr",
[(set RC:$r, (load ADDR:$addr))]>;
}
let mayStore=1 in {
def WRIT : HP41MCODEInst<0x068, (outs), (ins MEM:$addr,
RC:$r), "WRIT $addr", [(store RC:$r, ADDR:$addr)]>;
}
...
bool
HP41MCODERegisterInfo::eliminateFrameIndex(
MachineBasicBlock::iterator II,
int SPAdj, unsigned FIOperandNum,
RegScavenger *RS) const {
MachineInstr &MI = *II;
int FrameIndex = MI.getOperand(FIOperandNum).getIndex();
MachineFunction &MF = *MI.getParent()->getParent();
const HP41MCODEFrameLowering *TFI = getFrameLowering(MF);
llvm::Register FrameReg;
int Offset;
Offset = TFI->getFrameIndexReference(MF, FrameIndex,
FrameReg).getFixed()/4+1;
MI.getOperand(FIOperandNum).ChangeToRegister(FrameReg,
false);
MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Offset);
return false;
}
...
void HP41MCODEInstPrinter::printMemOperand(const MCInst *MI,
int opNum,
const MCSubtargetInfo &STI,
raw_ostream &O, const char *Modifier) {
const MCOperand &MO = MI->getOperand(opNum+1);
if (MO.isImm()) {
O << format_decimal((int)MO.getImm(), 1); return; }
assert(MO.isExpr() &&
"Unknown operand kind in printMemOperand");
MO.getExpr()->print(O, &MAI);
}
...
bool HP41MCODEDAGToDAGISel::SelectADDR(SDValue Addr,
SDValue &Base, SDValue &Offset) {
if (FrameIndexSDNode *FIN = dyn_cast(Addr)) {
Base = CurDAG->getTargetFrameIndex(
FIN->getIndex(), TLI->getPointerTy(
CurDAG->getDataLayout()));
Offset = CurDAG->getTargetConstant(0, SDLoc(Addr),
MVT::i32);
return true;
}
return false;
}
Then we end up with:
.file "hello.c"
.text
.globl foo ! -- Begin function foo
.type foo,@function
foo: ! @foo
! %bb.0: ! %entry
LDI S&X HEX: 0AB
WRIT 2
C=0 ALL
WRIT 1
READ 2
RTN
.Lfunc_end0:
.size foo, .Lfunc_end0-foo
! -- End function
.ident "clang version 20.0.0git (https://github.com/llvm/llvm-project.git ea1dfd50bfdfbd75969fd7653bc71c81f2a2350f)"
.section ".note.GNU-stack"
.addrsig
LLVM Exercise IV
How about calling another function:
int foo() {
return 0xFF;
}
int bar() {
return foo();
}
Code snippets:
...
def CC_HP41MCODE : CallingConv<[]>;
...
def SDT_HP41MCODENCXQ : SDTypeProfile<0, -1,
[SDTCisVT<0, iPTR>]>;
def HP41MCODENCXQ : SDNode<"HP41MCODEISD::NCXQ",
SDT_HP41MCODENCXQ,
[SDNPHasChain, SDNPOptInGlue,
SDNPOutGlue, SDNPVariadic]>;
...
def calltarget : Operand;
...
let isCall=1 in {
def NCXQ : HP41MCODEInst<0x001, (outs),
(ins calltarget:$addr),
"?NC XQ $addr",
[(HP41MCODENCXQ
tglobaladdr:$addr)]>;
}
...
NCXQ, // A call instruction.
...
SDValue
HP41MCODETargetLowering::LowerCall(
TargetLowering::CallLoweringInfo &CLI,
SmallVectorImpl &InVals) const {
...
CCInfo.AnalyzeCallOperands(Outs, CC_HP41MCODE);
...
Chain = DAG.getNode(HP41MCODEISD::NCXQ, DL, NodeTys, Ops);
...
return Chain;
}
...
The following assembly is produced:
.file "hello.c"
.text
.globl foo ! -- Begin function foo
.type foo,@function
foo: ! @foo
! %bb.0: ! %entry
LDI S&X HEX: 0FF
RTN
.Lfunc_end0:
.size foo, .Lfunc_end0-foo
! -- End function
.globl bar ! -- Begin function bar
.type bar,@function
bar: ! @bar
! %bb.0: ! %entry
?NC XQ foo
RTN
.Lfunc_end1:
.size bar, .Lfunc_end1-bar
! -- End function
.ident "clang version 20.0.0git (https://github.com/llvm/llvm-project.git ea1dfd50bfdfbd75969fd7653bc71c81f2a2350f)"
.section ".note.GNU-stack"
.addrsig
.addrsig_sym foo