mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-11-27 19:50:10 +00:00
Implemented initial KickAsm segment support. #113
This commit is contained in:
parent
9a54c0f814
commit
8f0b9c886f
60
src/main/java/dk/camelot64/kickc/asm/AsmFile.java
Normal file
60
src/main/java/dk/camelot64/kickc/asm/AsmFile.java
Normal file
@ -0,0 +1,60 @@
|
||||
package dk.camelot64.kickc.asm;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/** Define a KickAss output file */
|
||||
public class AsmFile implements AsmLine {
|
||||
|
||||
private final String name;
|
||||
private final Map<String,String> parameters;
|
||||
private int index;
|
||||
|
||||
public AsmFile(String name, Map<String, String> parameters) {
|
||||
this.name = name;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public AsmFile(String name) {
|
||||
this(name, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
public AsmFile param(String paramName, String paramValue) {
|
||||
parameters.put(paramName, paramValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineBytes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLineCycles() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsm() {
|
||||
StringBuffer asm = new StringBuffer();
|
||||
asm.append(".file ").append(" [ ");
|
||||
asm.append("name=\"").append(name).append("\"");
|
||||
for(String paramName : parameters.keySet()) {
|
||||
asm.append(",");
|
||||
String paramValue = parameters.get(paramName);
|
||||
asm.append(paramName).append("=").append(paramValue);
|
||||
}
|
||||
asm.append(" ]");
|
||||
return asm.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
}
|
64
src/main/java/dk/camelot64/kickc/asm/AsmSegment.java
Normal file
64
src/main/java/dk/camelot64/kickc/asm/AsmSegment.java
Normal file
@ -0,0 +1,64 @@
|
||||
package dk.camelot64.kickc.asm;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/** Selects a KickAss segment (defined using AsmSegmentDef)*/
|
||||
public class AsmSegment implements AsmLine {
|
||||
|
||||
private final String name;
|
||||
private final Map<String,String> parameters;
|
||||
private int index;
|
||||
|
||||
public AsmSegment(String name, Map<String, String> parameters) {
|
||||
this.name = name;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public AsmSegment(String name) {
|
||||
this(name, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
public AsmSegment param(String paramName, String paramValue) {
|
||||
parameters.put(paramName, paramValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineBytes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLineCycles() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsm() {
|
||||
StringBuffer asm = new StringBuffer();
|
||||
asm.append(".segment ").append(name);
|
||||
if(parameters!=null && parameters.size()>0) {
|
||||
asm.append(" [ ");
|
||||
boolean first = true;
|
||||
for(String paramName : parameters.keySet()) {
|
||||
if(!first) asm.append(",");
|
||||
first = false;
|
||||
String paramValue = parameters.get(paramName);
|
||||
asm.append(paramName).append("=").append(paramValue);
|
||||
}
|
||||
asm.append(" ]");
|
||||
}
|
||||
return asm.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
}
|
61
src/main/java/dk/camelot64/kickc/asm/AsmSegmentDef.java
Normal file
61
src/main/java/dk/camelot64/kickc/asm/AsmSegmentDef.java
Normal file
@ -0,0 +1,61 @@
|
||||
package dk.camelot64.kickc.asm;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/** Define a KickAss segment */
|
||||
public class AsmSegmentDef implements AsmLine {
|
||||
|
||||
private final String name;
|
||||
private final Map<String,String> parameters;
|
||||
private int index;
|
||||
|
||||
public AsmSegmentDef(String name, Map<String, String> parameters) {
|
||||
this.name = name;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public AsmSegmentDef(String name) {
|
||||
this(name, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
public AsmSegmentDef param(String paramName, String paramValue) {
|
||||
parameters.put(paramName, paramValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineBytes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLineCycles() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsm() {
|
||||
StringBuffer asm = new StringBuffer();
|
||||
asm.append(".segmentdef ").append(name).append(" [ ");
|
||||
boolean first = true;
|
||||
for(String paramName : parameters.keySet()) {
|
||||
if(!first) asm.append(",");
|
||||
first = false;
|
||||
String paramValue = parameters.get(paramName);
|
||||
asm.append(paramName).append("=").append(paramValue);
|
||||
}
|
||||
asm.append(" ]");
|
||||
return asm.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package dk.camelot64.kickc.asm;
|
||||
|
||||
import dk.camelot64.kickc.fragment.AsmFormat;
|
||||
|
||||
/** Set the program counter */
|
||||
public class AsmSetPc implements AsmLine {
|
||||
|
||||
|
@ -6,8 +6,12 @@ package dk.camelot64.kickc.model;
|
||||
public enum TargetPlatform {
|
||||
/** Commodore 64 with BASIC upstart SYS-command. */
|
||||
C64BASIC("c64basic"),
|
||||
/** Commodore 64 with BASIC upstart SYS-command - using Code and Data segments. */
|
||||
C64BASIC_SEGMENTS("c64basic_segments"),
|
||||
/** 6502 assembler (with no upstart code.)*/
|
||||
ASM6502("asm6502");
|
||||
ASM6502("asm6502"),
|
||||
/** 6502 assembler (with no upstart code.)*/
|
||||
ASM6502_SEGMENTS("asm6502_segments");
|
||||
|
||||
/** The default target platform. */
|
||||
public static final TargetPlatform DEFAULT = C64BASIC;
|
||||
|
@ -84,16 +84,41 @@ public class Pass4CodeGeneration {
|
||||
if(TargetPlatform.C64BASIC.equals(program.getTargetPlatform())) {
|
||||
programPc = 0x080d;
|
||||
} else {
|
||||
programPc = 0x1000;
|
||||
programPc = 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
asm.startChunk(currentScope, null, "Upstart");
|
||||
if(TargetPlatform.C64BASIC.equals(program.getTargetPlatform())) {
|
||||
if(TargetPlatform.C64BASIC_SEGMENTS.equals(program.getTargetPlatform())) {
|
||||
useSegments = true;
|
||||
currentCodeSegmentName = "Code";
|
||||
currentDataSegmentName = "Data";
|
||||
asm.addLine(new AsmFile(program.getFileName() + ".prg").param("type", "\"prg\"").param("segments", "\"Program\""));
|
||||
asm.addLine(new AsmSegmentDef("Program").param("segments", "\"Basic,Code,Data\""));
|
||||
asm.addLine(new AsmSegmentDef("Basic").param("start", "$0801"));
|
||||
asm.addLine(new AsmSegmentDef("Code").param("start", AsmFormat.getAsmNumber(programPc)));
|
||||
asm.addLine(new AsmSegmentDef("Data").param("startAfter", "\"Code\""));
|
||||
asm.addLine(new AsmSegment("Basic"));
|
||||
asm.addLine(new AsmBasicUpstart("bbegin"));
|
||||
setCurrentSegment(currentCodeSegmentName, asm);
|
||||
} else if(TargetPlatform.ASM6502_SEGMENTS.equals(program.getTargetPlatform())) {
|
||||
useSegments = true;
|
||||
currentCodeSegmentName = "Code";
|
||||
currentDataSegmentName = "Data";
|
||||
asm.addLine(new AsmFile(program.getFileName() + ".prg").param("type", "\"prg\"").param("segments", "\"Program\""));
|
||||
asm.addLine(new AsmSegmentDef("Program").param("segments", "\"Code,Data\""));
|
||||
asm.addLine(new AsmSegmentDef("Code").param("start", AsmFormat.getAsmNumber(programPc)));
|
||||
asm.addLine(new AsmSegmentDef("Data").param("startAfter", "\"Code\""));
|
||||
setCurrentSegment(currentCodeSegmentName, asm);
|
||||
} else if(TargetPlatform.ASM6502.equals(program.getTargetPlatform())) {
|
||||
useSegments = false;
|
||||
asm.addLine(new AsmSetPc("Program", AsmFormat.getAsmNumber(programPc)));
|
||||
} else if(TargetPlatform.C64BASIC.equals(program.getTargetPlatform())) {
|
||||
useSegments = false;
|
||||
asm.addLine(new AsmSetPc("Basic", AsmFormat.getAsmNumber(0x0801)));
|
||||
asm.addLine(new AsmBasicUpstart("bbegin"));
|
||||
asm.addLine(new AsmSetPc("Program", AsmFormat.getAsmNumber(programPc)));
|
||||
}
|
||||
asm.addLine(new AsmSetPc("Program", AsmFormat.getAsmNumber(programPc)));
|
||||
|
||||
// Generate global ZP labels
|
||||
asm.startChunk(currentScope, null, "Global Constants & labels");
|
||||
@ -104,6 +129,7 @@ public class Pass4CodeGeneration {
|
||||
// The current block is in a different scope. End the old scope.
|
||||
generateScopeEnding(asm, currentScope);
|
||||
currentScope = block.getScope();
|
||||
setCurrentSegment(currentCodeSegmentName, asm);
|
||||
asm.startChunk(currentScope, null, block.getLabel().getFullName());
|
||||
// Add any procedure comments
|
||||
if(block.isProcedureEntry(program)) {
|
||||
@ -120,7 +146,6 @@ public class Pass4CodeGeneration {
|
||||
}
|
||||
|
||||
generateComments(asm, block.getComments());
|
||||
|
||||
// Generate entry points (if needed)
|
||||
genBlockEntryPoints(asm, block);
|
||||
|
||||
@ -160,6 +185,9 @@ public class Pass4CodeGeneration {
|
||||
|
||||
currentScope = ScopeRef.ROOT;
|
||||
asm.startChunk(currentScope, null, "File Data");
|
||||
if(hasData(currentScope)) {
|
||||
setCurrentSegment(currentDataSegmentName, asm);
|
||||
}
|
||||
addData(asm, ScopeRef.ROOT);
|
||||
// Add all absolutely placed inline KickAsm
|
||||
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||
@ -181,6 +209,28 @@ public class Pass4CodeGeneration {
|
||||
program.setAsm(asm);
|
||||
}
|
||||
|
||||
// Should the generated program use segments?
|
||||
boolean useSegments = false;
|
||||
// Name of the current data segment
|
||||
private String currentCodeSegmentName = "Code";
|
||||
// Name of the current code segment
|
||||
private String currentDataSegmentName = "Data";
|
||||
// Name of the current active segment
|
||||
private String currentSegmentName = "";
|
||||
|
||||
/**
|
||||
* Set the current ASM segment - if needed
|
||||
*
|
||||
* @param segmentName The segment name we want
|
||||
* @param asm The ASM program (where a .segment line is added if needed)
|
||||
*/
|
||||
private void setCurrentSegment(String segmentName, AsmProgram asm) {
|
||||
if(useSegments && !currentSegmentName.equals(segmentName)) {
|
||||
asm.addLine(new AsmSegment(segmentName));
|
||||
currentSegmentName = segmentName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ASM names of variables being used for indirect calls in the current scope (procedure).
|
||||
* These will all be added as indirect JMP's at the end of the procedure scope.
|
||||
@ -201,6 +251,9 @@ public class Pass4CodeGeneration {
|
||||
asm.addInstruction("jmp", AsmAddressingMode.IND, indirectCallAsmName, false);
|
||||
}
|
||||
indirectCallAsmNames = new ArrayList<>();
|
||||
if(hasData(currentScope)) {
|
||||
setCurrentSegment(currentDataSegmentName, asm);
|
||||
}
|
||||
addData(asm, currentScope);
|
||||
asm.addScopeEnd();
|
||||
}
|
||||
@ -391,6 +444,23 @@ public class Pass4CodeGeneration {
|
||||
return useLabel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Examine whether there are any data directives to be added
|
||||
*
|
||||
* @param scopeRef The scope
|
||||
*/
|
||||
private boolean hasData(ScopeRef scopeRef) {
|
||||
Scope scope = program.getScope().getScope(scopeRef);
|
||||
Collection<ConstantVar> scopeConstants = scope.getAllConstants(false);
|
||||
for(ConstantVar constantVar : scopeConstants) {
|
||||
if(hasData(constantVar)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add data directives for constants declarations
|
||||
*
|
||||
@ -952,8 +1022,8 @@ public class Pass4CodeGeneration {
|
||||
* @param encodings The encodings to ensure
|
||||
*/
|
||||
private void ensureEncoding(AsmProgram asm, Collection<ConstantString.Encoding> encodings) {
|
||||
if(encodings == null || encodings.size()==0) return;
|
||||
if(encodings.size()>1) {
|
||||
if(encodings == null || encodings.size() == 0) return;
|
||||
if(encodings.size() > 1) {
|
||||
throw new CompileError("Different character encodings in one ASM statement not supported!");
|
||||
}
|
||||
// Size is 1 - grab it!
|
||||
@ -1002,7 +1072,6 @@ public class Pass4CodeGeneration {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get phi transitions for a specific to-block.
|
||||
*
|
||||
|
@ -1,6 +1,7 @@
|
||||
package dk.camelot64.kickc.passes;
|
||||
|
||||
import dk.camelot64.kickc.asm.*;
|
||||
import dk.camelot64.kickc.fragment.AsmFormat;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
@ -34,7 +35,7 @@ public class Pass5DoubleJumpElimination extends Pass5AsmOptimization {
|
||||
currentLabel = ((AsmLabel) line).getLabel();
|
||||
} else if(line instanceof AsmComment || line instanceof AsmConstant || line instanceof AsmLabelDecl) {
|
||||
// ignore
|
||||
} else if(line instanceof AsmBasicUpstart || line instanceof AsmDataNumeric || line instanceof AsmDataFill || line instanceof AsmDataString || line instanceof AsmDataAlignment || line instanceof AsmSetPc || line instanceof AsmInlineKickAsm|| line instanceof AsmSetEncoding|| line instanceof AsmDataKickAsm) {
|
||||
} else if(line instanceof AsmBasicUpstart || line instanceof AsmDataNumeric || line instanceof AsmDataFill || line instanceof AsmDataString || line instanceof AsmDataAlignment || line instanceof AsmSetPc || line instanceof AsmInlineKickAsm|| line instanceof AsmSetEncoding|| line instanceof AsmDataKickAsm|| line instanceof AsmSegmentDef|| line instanceof AsmSegment|| line instanceof AsmFile) {
|
||||
currentLabel = null;
|
||||
} else if(line instanceof AsmInstruction) {
|
||||
if(currentLabel != null) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
byte[15] fibs = $1100;
|
||||
byte[15] fibs;
|
||||
|
||||
void main() {
|
||||
fibs[0] = 0;
|
||||
|
@ -1,7 +1,6 @@
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
.label fibs = $1100
|
||||
main: {
|
||||
lda #0
|
||||
sta fibs
|
||||
@ -18,3 +17,4 @@ main: {
|
||||
bcc b1
|
||||
rts
|
||||
}
|
||||
fibs: .fill $f, 0
|
||||
|
@ -2,7 +2,7 @@ Culled Empty Block (label) main::@2
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
@begin: scope:[] from
|
||||
(byte[$f]) fibs#0 ← ((byte[$f])) (number) $1100
|
||||
(byte[$f]) fibs#0 ← { fill( $f, 0) }
|
||||
to:@1
|
||||
main: scope:[main] from @1
|
||||
*((byte[$f]) fibs#0 + (number) 0) ← (number) 0
|
||||
@ -59,12 +59,10 @@ Adding number conversion cast (unumber) 1 in (number~) main::$1 ← (byte) main:
|
||||
Adding number conversion cast (unumber) main::$1 in (number~) main::$1 ← (byte) main::i#2 + (unumber)(number) 1
|
||||
Adding number conversion cast (unumber) $f in (bool~) main::$3 ← (byte) main::i#1 < (number) $f
|
||||
Successful SSA optimization PassNAddNumberTypeConversions
|
||||
Inlining cast (byte[$f]) fibs#0 ← (byte[$f])(number) $1100
|
||||
Inlining cast *((byte[$f]) fibs#0 + (unumber)(number) 0) ← (unumber)(number) 0
|
||||
Inlining cast *((byte[$f]) fibs#0 + (unumber)(number) 1) ← (unumber)(number) 1
|
||||
Inlining cast (byte) main::i#0 ← (unumber)(number) 0
|
||||
Successful SSA optimization Pass2InlineCast
|
||||
Simplifying constant pointer cast (byte*) 4352
|
||||
Simplifying constant integer cast 0
|
||||
Simplifying constant integer cast 0
|
||||
Simplifying constant integer cast 1
|
||||
@ -87,7 +85,9 @@ Inferred type updated to byte in (unumber~) main::$0 ← (byte) main::i#2 + (byt
|
||||
Inferred type updated to byte in (unumber~) main::$1 ← (byte) main::i#2 + (byte) 1
|
||||
Simple Condition (bool~) main::$3 [11] if((byte) main::i#1<(byte) $f) goto main::@1
|
||||
Successful SSA optimization Pass2ConditionalJumpSimplification
|
||||
Constant (const byte[$f]) fibs#0 = (byte*) 4352
|
||||
Constant right-side identified [0] (byte[$f]) fibs#0 ← { fill( $f, 0) }
|
||||
Successful SSA optimization Pass2ConstantRValueConsolidation
|
||||
Constant (const byte[$f]) fibs#0 = { fill( $f, 0) }
|
||||
Constant (const byte) main::i#0 = 0
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Simplifying expression containing zero fibs#0 in [1] *((const byte[$f]) fibs#0 + (byte) 0) ← (byte) 0
|
||||
@ -169,7 +169,6 @@ Target platform is c64basic
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
.label fibs = $1100
|
||||
// @begin
|
||||
bbegin:
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
@ -229,6 +228,7 @@ main: {
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
fibs: .fill $f, 0
|
||||
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
Statement [4] *((const byte[$f]) fibs#0) ← (byte) 0 [ ] ( main:2 [ ] ) always clobbers reg byte a
|
||||
@ -255,7 +255,6 @@ ASSEMBLER BEFORE OPTIMIZATION
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
.label fibs = $1100
|
||||
// @begin
|
||||
bbegin:
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
@ -307,6 +306,7 @@ main: {
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
fibs: .fill $f, 0
|
||||
|
||||
ASSEMBLER OPTIMIZATIONS
|
||||
Removing instruction jmp b1
|
||||
@ -337,7 +337,7 @@ FINAL SYMBOL TABLE
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(byte[$f]) fibs
|
||||
(const byte[$f]) fibs#0 fibs = (byte*) 4352
|
||||
(const byte[$f]) fibs#0 fibs = { fill( $f, 0) }
|
||||
(void()) main()
|
||||
(byte~) main::$2 reg byte a 22.0
|
||||
(label) main::@1
|
||||
@ -359,7 +359,6 @@ Score: 263
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
.label fibs = $1100
|
||||
// @begin
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
// @1
|
||||
@ -403,4 +402,5 @@ main: {
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
fibs: .fill $f, 0
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(byte[$f]) fibs
|
||||
(const byte[$f]) fibs#0 fibs = (byte*) 4352
|
||||
(const byte[$f]) fibs#0 fibs = { fill( $f, 0) }
|
||||
(void()) main()
|
||||
(byte~) main::$2 reg byte a 22.0
|
||||
(label) main::@1
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Tests the target platform ASM6502
|
||||
.pc = $1000 "Program"
|
||||
.pc = $2000 "Program"
|
||||
main: {
|
||||
ldx #0
|
||||
b2:
|
||||
|
@ -143,7 +143,7 @@ Target platform is asm6502
|
||||
// File Comments
|
||||
// Tests the target platform ASM6502
|
||||
// Upstart
|
||||
.pc = $1000 "Program"
|
||||
.pc = $2000 "Program"
|
||||
// Global Constants & labels
|
||||
// @begin
|
||||
bbegin:
|
||||
@ -212,7 +212,7 @@ ASSEMBLER BEFORE OPTIMIZATION
|
||||
// File Comments
|
||||
// Tests the target platform ASM6502
|
||||
// Upstart
|
||||
.pc = $1000 "Program"
|
||||
.pc = $2000 "Program"
|
||||
// Global Constants & labels
|
||||
// @begin
|
||||
bbegin:
|
||||
@ -311,7 +311,7 @@ Score: 161
|
||||
// File Comments
|
||||
// Tests the target platform ASM6502
|
||||
// Upstart
|
||||
.pc = $1000 "Program"
|
||||
.pc = $2000 "Program"
|
||||
// Global Constants & labels
|
||||
// @begin
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
|
Loading…
Reference in New Issue
Block a user