0 에서 컴 파일 러(12):코드 생 성의 생 성 논리

프로젝트 의 전체 코드 는 C2j-copiler 에 있 습 니 다.
머리말
지난 편 에서 기본 적 인 자바 바이트 코드 명령 을 설명 한 후에 본 격 적 으로 진정한 코드 생 성 부분 에 들 어 갈 수 있다.그러나 이 부분 에서 먼저 말 한 것 은 코드 생 성 에 의존 하 는 몇 가지 유형,즉 명령 을 생 성 하 는 작업 이다.
이 파일 은 모두 codegen 아래 에 있 습 니 다.
  • Directive.java
  • Instruction.java
  • CodeGenerator.java
  • ProgramGenerator.java

  • Directive.java
    이것 은 매 거 진 클래스 로 비교적 특수 한 명령 을 만 드 는 데 쓰 인 다.
    하나의 클래스 나 하나의 방법 을 설명 하 는 범위 의 명령 을 만 드 는 것 은 비교적 간단 하 다.
    public enum Directive {
        CLASS_PUBLIC(".class public"),
        END_CLASS(".end class"),
        SUPER(".super"),
        FIELD_PRIVATE_STATIC(".field private static"),
        METHOD_STATIC(".method static"),
        METHOD_PUBLIC(".method public"),
        FIELD_PUBLIC(".field public"),
        METHOD_PUBBLIC_STATIC(".method public static"),
        END_METHOD(".end method"),
        LIMIT_LOCALS(".limit locals"),
        LIMIT_STACK(".limit stack"),
        VAR(".var"),
        LINE(".line");
        
        private String text;
        
        Directive(String text) {
            this.text = text;
        }
        
        public String toString() {
            return text;
        }
    }

    Instruction.java
    이것 도 하나의 매 거 류 로 기본 적 인 명령 을 만 드 는 데 쓰 인 다.
    public enum Instruction {
        LDC("ldc"),
        
        GETSTATIC("getstatic"),
        SIPUSH("sipush"),
        IADD("iadd"),
        IMUL("imul"),
        ISUB("isub"),
        IDIV("idiv"),
        INVOKEVIRTUAL("invokevirtual"),
        INVOKESTATIC("invokestatic"),
        INVOKESPECIAL("invokespecial"),
        RETURN("return"),
        IRETURN("ireturn"),
        ILOAD("iload"),
        ISTORE("istore"),
        NEWARRAY("newarray"),
        NEW("new"),
        DUP("dup"),
        ASTORE("astore"),
        IASTORE("iastore"),
        ALOAD("aload"),
        PUTFIELD("putfield"),
        GETFIELD("getfield"),
        ANEWARRAY("anewarray"),
        AASTORE("aastore"),
        AALOAD("aaload"),
        IF_ICMPEG("if_icmpeq"),  
        IF_ICMPNE("if_icmpne"),
        IF_ICMPLT("if_icmplt"),
        IF_ICMPGE("if_icmpge"),
        IF_ICMPGT("if_icmpgt"),
        IF_ICMPLE("if_icmple"),
        GOTO("goto"),
        IALOAD("iaload");
        
        private String text;
        Instruction(String s) {
            this.text = s;
        }
        
        public String toString() {
            return text;
        }
    }

    CodeGenerator.java
    중요 한 것 은 생 성 된 논 리 는 주로 CodeGenerator 와 ProgramGenerator 에 있 고 CodeGenerator 는 ProgramGenerator 의 부류 이다.
    CodeGenerator 의 구조 함수 new 는 xxx.j 에 바이트 코드 를 출력 하 는 출력 흐름 을 만 들 었 습 니 다.
    public CodeGenerator() {
          String assemblyFileName = programName + ".j";
    
          try {
              bytecodeFile = new PrintWriter(new PrintStream(new
                      File(assemblyFileName)));
          } catch (FileNotFoundException e) {
              e.printStackTrace();
          }
      }

    emit,emitString,emitDirective,emitBlankLine 은 모두 출력 기본 명령 에 속 하 며,서로 다른 조작 과 조작 수 에 대응 하 는 여러 가지 재 로드 방법 이 있다.주의해 야 할 것 은 어떤 명령 은 먼저 캐 시 를 해 야 할 수도 있 습 니 다.마지막 에 같이 제출 해 야 합 니 다.예 를 들 어 buffered,classDefine 은 먼저 캐 시 해 야 할 불 값 인지 아 닌 지 를 판단 하 는 데 사 용 됩 니 다.
    public void emitString(String s) {
        if (buffered) {
            bufferedContent += s + "
    "; return; } if (classDefine) { classDefinition += s + "
    "; return; } bytecodeFile.print(s); bytecodeFile.flush(); } public void emit(Instruction opcode) { if (buffered) { bufferedContent += "\t" + opcode.toString() + "
    "; return; } if (classDefine) { classDefinition += "\t" + opcode.toString() + "
    "; return; } bytecodeFile.println("\t" + opcode.toString()); bytecodeFile.flush(); ++instructionCount; } public void emitDirective(Directive directive, String operand1, String operand2, String operand3) { if (buffered) { bufferedContent += directive.toString() + " " + operand1 + " " + operand2 + " " + operand3 + "
    "; return; } if (classDefine) { classDefinition += directive.toString() + " " + operand1 + " " + operand2 + " " + operand3 + "
    "; return; } bytecodeFile.println(directive.toString() + " " + operand1 + " " + operand2 + " " + operand3); ++instructionCount; } public void emitBlankLine() { if (buffered) { bufferedContent += "
    "; return; } if (classDefine) { classDefinition += "
    "; return; } bytecodeFile.println(); bytecodeFile.flush(); }

    ProgramGenerator.java
    ProgramGenerator 는 CodeGenerator 를 계승 했다.즉,기본 적 인 조작 을 계승 했다.이전 구조 체,배열 의 명령 출력 은 모두 이런 종류 에 있 었 다.
    처리 플러그 인
    먼저 네 개의 속성 을 보 세 요.이 네 개의 속성 은 주로 포 함 된 분기 와 순환 을 처리 합 니 다.
    private int branch_count = 0;
    private int branch_out = 0;
    private String embedded = "";
    private int loopCount = 0;

    4
  • ifelse 문 구 를 끼 워 넣 지 않 았 을 때 embedded 속성 은 문자'i'를 추가 하고 한 가 지 를 종료 할 때 이'i'를 잘 라 냅 니 다
  • branch_count 와 branchout 은 모두 같은 작용 역 의 분기 도약 을 표시 하 는 데 쓰 인 다
  • 4
  • 즉,끼 워 넣 은 것 이 있 으 면 embedded 로 처리 하고,하나의 작용 역 의 가 지 를 사용 하 는 것 이 라면 branchcount 와 branchout 로 고 를 하 겠 습 니 다.
    public void incraseIfElseEmbed() {
        embedded += "i";
    }
    
    public void decraseIfElseEmbed() {
        embedded = embedded.substring(1);
    }
    
    public void emitBranchOut() {
        String s = "
    " + embedded + "branch_out" + branch_out + ":
    "; this.emitString(s); branch_out++; }

    loopCount 는 끼 워 넣 기 순환 에 대한 처리 입 니 다.
    public void emitLoopBranch() {
        String s = "
    " + "loop" + loopCount + ":" + "
    "; emitString(s); } public String getLoopBranch() { return "loop" + loopCount; } public void increaseLoopCount() { loopCount++; }

    처리 구조 체
    putStructToClassDeclaration 은 구조 체,즉 new 클래스 를 정의 합 니 다.declaresStructAsClass 는 처리 구조 체 의 변수,즉 처리 류 에 해당 하 는 속성 입 니 다.
  • 구조 체 가 이미 클래스 의 정 의 를 내 렸 다 면 structNameist 에 가입 하고 중복 되 는 정 의 를 하지 않 습 니 다
  • symbol.getValueSetter()가 비어 있 지 않 으 면 구조 체 배열 임 을 나타 낸다.그러면 배열 에서 이 인 스 턴 스 를 직접 불 러 오고 스 택 에 만 들 지 않 아 도 된다
  • declaresStructAsClass 는 전편 에서 말 한 자바 바이트 코드 와 관련 된 명령 에 따라 클래스 를 만 듭 니 다
  • public void putStructToClassDeclaration(Symbol symbol) {
        Specifier sp = symbol.getSpecifierByType(Specifier.STRUCTURE);
        if (sp == null) {
            return;
        }
    
        StructDefine struct = sp.getStruct();
        if (structNameList.contains(struct.getTag())) {
            return;
        } else {
            structNameList.add(struct.getTag());
        }
    
        if (symbol.getValueSetter() == null) {
            this.emit(Instruction.NEW, struct.getTag());
            this.emit(Instruction.DUP);
            this.emit(Instruction.INVOKESPECIAL, struct.getTag() + "/" + "()V");
            int idx = this.getLocalVariableIndex(symbol);
            this.emit(Instruction.ASTORE, "" + idx);
        }
    
        declareStructAsClass(struct);
    }
    
    private void declareStructAsClass(StructDefine struct) {
        this.setClassDefinition(true);
    
        this.emitDirective(Directive.CLASS_PUBLIC, struct.getTag());
        this.emitDirective(Directive.SUPER, "java/lang/Object");
    
        Symbol fields = struct.getFields();
        do {
            String fieldName = fields.getName() + " ";
            if (fields.getDeclarator(Declarator.ARRAY) != null) {
                fieldName += "[";
            }
    
            if (fields.hasType(Specifier.INT)) {
                fieldName += "I";
            } else if (fields.hasType(Specifier.CHAR)) {
                fieldName += "C";
            } else if (fields.hasType(Specifier.CHAR) && fields.getDeclarator(Declarator.POINTER) != null) {
                fieldName += "Ljava/lang/String;";
            }
    
            this.emitDirective(Directive.FIELD_PUBLIC, fieldName);
            fields = fields.getNextSymbol();
        } while (fields != null);
    
        this.emitDirective(Directive.METHOD_PUBLIC, "()V");
        this.emit(Instruction.ALOAD, "0");
        String superInit = "java/lang/Object/()V";
        this.emit(Instruction.INVOKESPECIAL, superInit);
    
        fields = struct.getFields();
        do {
            this.emit(Instruction.ALOAD, "0");
            String fieldName = struct.getTag() + "/" + fields.getName();
            String fieldType = "";
            if (fields.hasType(Specifier.INT)) {
                fieldType = "I";
                this.emit(Instruction.SIPUSH, "0");
            } else if (fields.hasType(Specifier.CHAR)) {
                fieldType = "C";
                this.emit(Instruction.SIPUSH, "0");
            } else if (fields.hasType(Specifier.CHAR) && fields.getDeclarator(Declarator.POINTER) != null) {
                fieldType = "Ljava/lang/String;";
                this.emit(Instruction.LDC, " ");
            }
    
            String classField = fieldName + " " + fieldType;
            this.emit(Instruction.PUTFIELD, classField);
    
            fields = fields.getNextSymbol();
        } while (fields != null);
    
        this.emit(Instruction.RETURN);
        this.emitDirective(Directive.END_METHOD);
        this.emitDirective(Directive.END_CLASS);
    
        this.setClassDefinition(false);
    }

    스 택 정보 가 져 오기
    다른 자바 바이트 코드 는 모두 이전 편 에 따라 이 루어 집 니 다.논리 가 복잡 하지 않 습 니 다.지금 방법 을 보 겠 습 니 다:getLocalVariableIndex,이 방법 은 현재 대기 열 에 있 는 변 수 를 가 져 오 는 것 입 니 다.
    4.567917.현재 실 행 된 함 수 를 먼저 받 은 다음 에 함수 의 대응 하 는 매개 변 수 를 받 은 다음 에 반전(이것 은 매개 변수 스 택 의 순서 와 관계 가 있다)4.567917.그리고 현재 기호 가 대응 하 는 역할 역 의 기 호 를 모두 목록 에 추가 합 니 다4.567917.그 다음 에 이 목록 을 옮 겨 다 니 면 이 기호 가 대열 에 대응 하 는 위 치 를 계산 할 수 있 습 니 다
    public int getLocalVariableIndex(Symbol symbol) {
        TypeSystem typeSys = TypeSystem.getInstance();
        String funcName = nameStack.peek();
        Symbol funcSym = typeSys.getSymbolByText(funcName, 0, "main");
        ArrayList localVariables = new ArrayList<>();
        Symbol s = funcSym.getArgList();
        while (s != null) {
            localVariables.add(s);
            s = s.getNextSymbol();
        }
        Collections.reverse(localVariables);
    
        ArrayList list = typeSys.getSymbolsByScope(symbol.getScope());
        for (int i = 0; i < list.size(); i++) {
            if (!localVariables.contains(list.get(i))) {
                localVariables.add(list.get(i));
            }
        }
    
        for (int i = 0; i < localVariables.size(); i++) {
            if (localVariables.get(i) == symbol) {
                return i;
            }
        }
    
        return -1;
    }

    작은 매듭
    이 편 은 주로 전편 의 JVM 바이트 코드 에 따라 서로 다른 조작 에 대해 서로 다른 방법 으로 이 명령 을 출력 하 는 것 이다.
    환영 스타!
    다음으로 전송:https://www.cnblogs.com/secoding/p/11388347.html

    좋은 웹페이지 즐겨찾기