Skip to content

alsgh5016/ocaml-llvm-tutorial

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ocaml-llvm-tutorial

Install ocaml package in your Ubuntu System

bash -c "sh <(curl -fsSL https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh)"
opam init
opam switch create 4.11.1
eval $(opam env --switch=4.11.1)
opam install ocamlbuild ocamlfind
opam install llvm.14.0.6
  • opam env 버전은 적당히 골라서 사용하면 됨

    • part1, part2, part3, part4 Makefile에서 env 버전 경로만 정확하게 저장하면 됨
    ...
    OCAML_VERSION := 4.11.1 # write your env version
    LLVM_VERSION := 14 # write your llvm version
    ...
    export OCAMLPATH=$(HOME)/.opam/$(OCAML_VERSION)/lib/
    ...
    run: $(TOOLS) hello.bc
        CAML_LD_LIBRARY_PATH=$(OCAMLPATH)/stublibs/ ./build/tutorial01/src/tutorial01.byte hello.bc
    ...
  • opam install llvm 혹은 llvm.15.0.6 이상의 버전으로 설치하면, opam과 ocamlfind 사이에 version conflict가 발생함

Run Makefile in part1 (or 2-4) Directory

make run



part1 code

open Llvm

let _ =
  let llctx = Llvm.global_context () in
  let llmem = Llvm.MemoryBuffer.of_file Sys.argv.(1) in
  let llm = Llvm_bitreader.parse_bitcode llctx llmem in
  Llvm.dump_module llm ;
  ()
  • Makefile에서 .ml 파일을 컴파일한 .byte 실행 파일로 bitcode 분석 수행
    • make run
    • bitcode 파일명을 hello.bc와 다르게 사용하고 싶은 경우 Makefile 수정
    • part1 코드는 argv로 전달받은 bitcode를 dump (= print)



part2 code

let rec print_type llty =
  let ty = Llvm.classify_type llty in
  match ty with
  | Llvm.TypeKind.Integer  -> Printf.printf "  integer\n"
  | Llvm.TypeKind.Function -> Printf.printf "  function\n"
  | Llvm.TypeKind.Array    -> Printf.printf "  array of" ; print_type (Llvm.element_type llty)
  | Llvm.TypeKind.Pointer  -> Printf.printf "  pointer to" ; print_type (Llvm.element_type llty)
  | Llvm.TypeKind.Vector   -> Printf.printf "  vector of" ; print_type (Llvm.element_type llty)
  | _                      -> Printf.printf "  other type\n"

let print_val lv =
  Printf.printf "Value\n" ;
  Printf.printf "  name %s\n" (Llvm.value_name lv) ;
  let llty = Llvm.type_of lv in
  Printf.printf "  type %s\n" (Llvm.string_of_lltype llty) ;
  print_type llty ;
  ()

let print_fun lv =
  Llvm.iter_blocks
    (fun llbb ->
      Printf.printf "  bb: %s\n" (Llvm.value_name (Llvm.value_of_block (llbb))) ;
      Llvm.iter_instrs
        (fun lli ->
          Printf.printf "    instr: %s\n" (Llvm.string_of_llvalue lli)
        )
        llbb
    )
    lv

let _ =
  let llctx = Llvm.global_context () in
  let llmem = Llvm.MemoryBuffer.of_file Sys.argv.(1) in
  let llm = Llvm_bitreader.parse_bitcode llctx llmem in
  (*Llvm.dump_module llm ;*)

  Printf.printf "*** lookup_function ***\n" ;
  let opt_lv = Llvm.lookup_function "main" llm in
  begin
  match opt_lv with
  | Some lv -> print_val lv
  | None    -> Printf.printf "'main' function not found\n"
  end ;

  Printf.printf "*** iter_functions ***\n" ;
  Llvm.iter_functions print_val llm ;

  Printf.printf "*** fold_left_functions ***\n" ;
  let count =
    Llvm.fold_left_functions
      (fun acc lv ->
        print_val lv ;
        acc + 1
      )
      0
      llm
  in
  Printf.printf "Functions count: %d\n" count ;

  Printf.printf "*** basic blocks/instructions ***\n" ;
  Llvm.iter_functions print_fun llm ;

  Printf.printf "*** iter_globals ***\n" ;
  Llvm.iter_globals print_val llm ;

  ()
  • part2 코드는 argv로 전달받은 bitcode 내 IR의 type을 print



part3 code

open Llvm

let _ =
  let llctx = global_context () in
  let llm = create_module llctx "mymodule" in

  let i32_t = i32_type llctx in
  let fty = function_type i32_t [| |] in

  let f = define_function "main" fty llm in
  let llbuilder = builder_at_end llctx (entry_block f) in

  let _ = build_ret (const_int i32_t 0) llbuilder in

  if Array.length Sys.argv > 1
  then print_module Sys.argv.(1) llm
  else dump_module llm ;
  ()
  • part3 코드는 module과 function을 생성하고, builder를 통해 bitcode 생성

; ModuleID = 'mymodule'
source_filename = "mymodule"

define i32 @main() {
entry:
  ret i32 0
}

part4 code

open Llvm

let add_target_triple triple llm =
  Llvm_X86.initialize ();
  let lltarget  = Llvm_target.Target.by_triple triple in
  let llmachine = Llvm_target.TargetMachine.create ~triple:triple lltarget in
  let lldly     = Llvm_target.TargetMachine.data_layout llmachine in

  set_target_triple (Llvm_target.TargetMachine.triple llmachine) llm ;
  set_data_layout (Llvm_target.DataLayout.as_string lldly) llm ;
  ()


let _ =
  let llctx = global_context () in
  let llm = create_module llctx "mymodule" in

  add_target_triple "x86_64" llm ;
  let i8_t = i8_type llctx in
  let i32_t = i32_type llctx in
  let fty = function_type i32_t [| |] in

  let f = define_function "main" fty llm in
  let llbuilder = builder_at_end llctx (entry_block f) in

  let printf_ty = var_arg_function_type i32_t [| pointer_type i8_t |] in
  let printf = declare_function "printf" printf_ty llm in
  add_function_attr printf Attribute.Nounwind ;
  add_param_attr (param printf 0) Attribute.Nocapture ;

  let s = build_global_stringptr "Hello, world!\n" "" llbuilder in
  (* try commenting these two lines and compare the result *)
  let zero = const_int i32_t 0 in
  let s = build_in_bounds_gep s [| zero |] "" llbuilder in

  let _ = build_call printf [| s |] "" llbuilder in

  let _ = build_ret (const_int i32_t 0) llbuilder in

  Llvm_analysis.assert_valid_module llm ;
  let _ =
    if Array.length Sys.argv > 1
    then Llvm_bitwriter.write_bitcode_file llm Sys.argv.(1) |> ignore
    else dump_module llm
  in
  ()
  • part4 코드는 module과 function을 생성하고, builder를 통해 bitcode 생성
  • main 함수에 "Hello World"를 printf 하는 IR 생성 및 삽입

  • Bitcode 파일을 분석하기 위해서는, Llvm 모듈 내의 OCaml API를 사용하여 코드 작성

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors