একটি লিস্প সংকলন: ল্যাম্বদা উত্তোলন

একটি লিস্প সংকলন: ল্যাম্বদা উত্তোলন


প্রথমপূর্ববর্তী

আমি ভাবিনি যে এই দিনটি আসবে, তবে আমি আবার গোলৌম টিউটোরিয়াল (পিডিএফ) তুলেছি এবং আমি আরও কিছুটা এগিয়ে এসেছি। কেবল একটি সতর্কতা আছে: আমি পাইথনে বাস্তবায়নটি আবার লিখেছি। এটি উপলব্ধ একই রেপো মধ্যে
সংকলক.পি। এটি সংক্ষিপ্ত, 300 টিরও বেশি লোক + পরীক্ষায় (সি সংস্করণের 1200 এলওসি + পরীক্ষার তুলনায়) কিছুটা আসছে।

আমি অনুমান করি যে আরও একটি সতর্কতাও রয়েছে, এটি হ’ল পাইথন সংস্করণে কোনও এস-এক্সপ্রেশন রিডার নেই। তবে এটি ঠিক আছে: প্রিয় পাঠক, এটি আপনার জন্য একটি অনুশীলন হিসাবে বিবেচনা করুন। এটি টিউটোরিয়ালটির সবচেয়ে আকর্ষণীয় অংশ খুব কমই।

ওহ, এবং আমি নির্দেশ এনকোডিংও ফেলে দিয়েছি। আমি এখন পাঠ্য সমাবেশ করছি। WOMP WOMP।

যাইহোক, কাগজে প্রয়োজনীয় হিসাবে ল্যাম্বডাস উত্তোলনের জন্য তিনটি জিনিস প্রয়োজন:

  • কোন ভেরিয়েবলগুলি আবদ্ধ তা ট্র্যাক করে রাখা
  • প্রদত্ত ল্যাম্বডায় কোন ভেরিয়েবলগুলি নিখরচায় তা ট্র্যাক করে রাখা
  • একটি চলমান তালিকা রাখা code আমরা পুনরাবৃত্তি হিসাবে যে অবজেক্টগুলি তৈরি করি

আমাদের দুটি ফর্ম রয়েছে যা ভেরিয়েবলগুলিকে আবদ্ধ করতে পারে: let এবং lambda। এর অর্থ হ’ল আমাদের সেই বিশেষ অভিব্যক্তিগুলির নামগুলি সনাক্ত করতে হবে এবং পরিবেশটি সংশোধন করতে হবে। কি পরিবেশ, আপনি জিজ্ঞাসা?

লিফটার

ঠিক আছে, আমি এই সামান্য আছে LambdaConverter ক্লাস।

class LambdaConverter:
    def __init__(self):
        self.labels: dict(str, list) = 

    def convert(self, expr, bound: set(str), free: set(str)):
        match expr:
            case _:
                raise NotImplementedError(expr)

def lift_lambdas(expr):
    conv = LambdaConverter()
    expr = conv.convert(expr, set(), set())
    labels = ((name, code) for name, code in conv.labels.items())
    return ("labels", labels, expr)

আমরা একই রাখি labels প্রোগ্রামটির পুরো পুনরাবৃত্ত ট্র্যাভার্সাল জন্য ডিক্ট, তবে আমরা সংশোধন করি bound প্রতিটি বাইন্ডিং সাইটে এবং free শুধুমাত্র ল্যাম্বডাসে।

সেগুলি কীভাবে ব্যবহৃত হয় তা চিত্রিত করতে, আসুন কিছু নমুনা অভিব্যক্তি পূরণ করি: 3,
'aএবং #t::

class LambdaConverter:
    # ...
    def convert(self, expr, bound, free):
        match expr:
            case int(_) | Char():  # bool(_) is implied by int(_)
                return expr
            # ...

class LambdaTests(unittest.TestCase):
    def test_int(self):
        self.assertEqual(lift_lambdas(3), ("labels", (), 3))

    def test_bool(self):
        self.assertEqual(lift_lambdas(True), ("labels", (), True))
        self.assertEqual(lift_lambdas(False), ("labels", (), False))

    def test_char(self):
        self.assertEqual(lift_lambdas(Char("a")), ("labels", (), Char("a")))

ঠিক আছে, ঠিক আছে, অবশ্যই, আমরা যখন সাধারণ ধ্রুবকগুলির সাথে কাজ করি তখন আমাদের আসলে পরিবর্তনশীল নামগুলি সম্পর্কে চিন্তা করার দরকার নেই।

সুতরাং আসুন ভেরিয়েবলগুলি দেখুন:

class LambdaConverter:
    # ...
    def convert(self, expr, bound, free):
        match expr:
            # ...
            case str(_) if expr in bound:
                return expr
            case str(_):
                free.add(expr)
                return expr
            # ...

class LambdaTests(unittest.TestCase):
    # ...
    def test_freevar(self):
        self.assertEqual(lift_lambdas("x"), ("labels", (), "x"))

আমরা আসলে ভেরিয়েবল ব্যবহারগুলিকে রূপান্তর করতে চাই না, কেবল তাদের ব্যবহার সম্পর্কে কিছু মেটাডেটা যুক্ত করুন। আমাদের যদি কিছু পরিবর্তনশীল থাকে x আবদ্ধ একটি let বা ক lambda
অভিব্যক্তি, আমরা এটি একা ছেড়ে যেতে পারি। অন্যথায়, আমাদের এটি চিহ্নিত করা দরকার।

(let ((x 5))
  (+ x        ; bound
     y))      ; free

এখানে একটি বিরক্তিকর বিশেষ কেস রয়েছে যা আমরা বিবেচনা করতে চাই না + (উদাহরণস্বরূপ) একটি নিখরচায় পরিবর্তনশীল হিসাবে: এটি একটি বিশেষ ভাষা আদিম। সুতরাং আমরা বিবেচনা + এবং অন্যরা সর্বদা আবদ্ধ হিসাবে।

class LambdaConverter:
    # ...
    def convert(self, expr, bound, free):
        match expr:
            # ...
            case str(_) if expr in BUILTINS:
                return expr
            # ...

class LambdaTests(unittest.TestCase):
    # ...
    def test_plus(self):
        self.assertEqual(lift_lambdas("+"), ("labels", (), "+"))

এই জ্ঞান দিয়ে সজ্জিত, আমরা আমাদের প্রথম পুনরাবৃত্ত ট্র্যাভারসাল করতে পারি: if
অভিব্যক্তি। যেহেতু তাদের পুনরাবৃত্ত অংশ রয়েছে এবং কোনও ভেরিয়েবলকে আবদ্ধ করবেন না, তাই তারা এই লিফটারের জন্য দ্বিতীয়-সিম্প্লেস্ট ফর্ম।

class LambdaConverter:
    # ...
    def convert(self, expr, bound, free):
        match expr:
            # ...
            case ("if", test, conseq, alt):
                return ("if",
                        self.convert(test, bound, free),
                        self.convert(conseq, bound, free),
                        self.convert(alt, bound, free))
            # ...

class LambdaTests(unittest.TestCase):
    # ...
    def test_if(self):
        self.assertEqual(lift_lambdas(("if", 1, 2, 3)),
                         ("labels", (), ("if", 1, 2, 3)))

এই পরীক্ষাটি এখনও আমাদের খুব বেশি কিছু বলে না (খালি যুক্ত করা ছাড়া অন্য labels এবং একটি ব্যতিক্রম উত্থাপন না)। তবে তা শীঘ্রই হবে।

ল্যাম্বদা

আসুন কি সম্পর্কে চিন্তা করা যাক lambda কি। এটি একটি পরিখা কোটে বৈশিষ্ট্যগুলির একগুচ্ছ:

  • নাম বাঁধুন
  • কোড বরাদ্দ
  • বাইরের পরিবেশ ক্যাপচার

উত্তোলন পরিচালনা করতে, আমাদের তিনটি সম্পর্কে যুক্তি দিতে হবে।

প্রথমত, ল্যাম্বদা তার পরামিতিগুলিকে নতুন নাম হিসাবে আবদ্ধ করে। আসলে, সেগুলি
শুধুমাত্র একটি ল্যাম্বডায় আবদ্ধ ভেরিয়েবল। বিবেচনা:

x সেই ল্যাম্বডায় একটি নিখরচায় পরিবর্তনশীল! আমরা সেই ল্যাম্বডাকে রূপান্তর করতে চাই:

;                  +-parameters
;                  |  +-freevars
;                  v  v
(labels ((f0 (code () (x) x)))
  (closure f0 x))

এমনকি যদি x কিছু দ্বারা আবদ্ধ ছিল let ল্যাম্বডার বাইরে, এটি ল্যাম্বডায় বিনামূল্যে হবে:

(let ((x 5))
  (lambda () x))

তার মানে আমরা এর মাধ্যমে থ্রেড করি না bound ল্যাম্বডা বডি প্যারামিটার; নামগুলি কী আবদ্ধ তা আমরা যত্ন করি না বাইরে ল্যাম্বদা

আমরা ল্যাম্বডার অভ্যন্তরে নিখরচায় থাকা ভেরিয়েবলের সেটগুলির উপর নজর রাখতে চাই: আমাদের তাদের একটি তৈরি করার প্রয়োজন হবে code ফর্ম। অতএব, আমরা ল্যাম্বডা বডি এর জন্য একটি নতুন সেটও পাস করি free সেট।

এখনও অবধি, এই সমস্ত পরিবেশের ঝাঁকুনি আমাদের দেয়:

class LambdaConverter:
    # ...
    def convert(self, expr, bound, free):
        match expr:
            # ...
            case ("lambda", params, body):
                body_free = set()
                body = self.convert(body, set(params), body_free)
                free.update(body_free - bound)
                # ...
                return # ???
            # ...

এছাড়াও আছে free.update(body_free - bound) সেখানে কারণ ল্যাম্বডা এক্সপ্রেশনটিতে কোনও পরিবর্তনশীল ফ্রি বর্তমান অভিব্যক্তিতেও নিখরচায় – ভাল, বর্তমানে আবদ্ধ ভেরিয়েবলগুলি বাদে।

সর্বশেষে, আমরা একটি করব code ফর্ম এবং ক closure ফর্ম। দ্য code একটি নতুন লেবেল সহ গ্লোবাল তালিকায় যুক্ত হয় এবং লেবেলটি থ্রেড হয়ে যায়
closure

class LambdaConverter:
    def push_label(self, params, freevars, body):
        result = f"flen(self.labels)"
        self.labels(result) = ("code", params, freevars, body)
        return result

    def convert(self, expr, bound, free):
        match expr:
            # ...
            case ("lambda", params, body):
                body_free = set()
                body = self.convert(body, set(params), body_free)
                free.update(body_free - bound)
                # vvvv new below this line vvvv
                body_free = sorted(body_free)
                label = self.push_label(params, body_free, body)
                return ("closure", label, *body_free)
            # ...

এটা ফিনিক! আমি মনে করি আমার প্রথম দু’টি সংস্করণ বিভিন্ন কারণে সূক্ষ্মভাবে ভুল ছিল। পরীক্ষাগুলি এখানে অনেক সাহায্য করে। কোডের প্রতিটি জায়গার জন্য যেখানে আমি গণ্ডগোল করি bound বা free একটি পুনরাবৃত্ত কলটিতে, আমি একটি পরীক্ষা করার চেষ্টা করেছি যা আমি ভুল হয়ে গেলে ব্যর্থ হবে।

class LambdaTests(unittest.TestCase):
    # ...
    def test_lambda_no_params_no_freevars(self):
        self.assertEqual(lift_lambdas(("lambda", (), 3)),
                         ("labels", (
                             ("f0", ("code", (), (), 3)),
                         ), ("closure", "f0")))

    def test_nested_lambda(self):
        self.assertEqual(lift_lambdas(("lambda", ("x"),
                                       ("lambda", ("y"),
                                        ("+", "x", "y")))),
                         ("labels",
                          (("f0", ("code", ("y"), ("x"), ("+", "x", "y"))),
                           ("f1", ("code", ("x"), (), ("closure", "f0", "x")))),
                          ("closure", "f1")))
    # ... and many more, especially interacting with `let`

এখন আসুন অন্য বাইন্ডার সম্পর্কে কথা বলা যাক।

যাক

আসুন কি সম্পর্কে চিন্তা করা যাক let একটি বিভ্রান্তিকর লেট এক্সপ্রেশন পরীক্ষা করে কি করে:

(let ((wolf 5)
      (x wolf))
  wolf)

এই অভিব্যক্তিতে দুটি আছে wolfএস। তাদের মধ্যে একটি লেট ভিতরে আবদ্ধ, তবে অন্যটি লেট ভিতরে বিনামূল্যে! এই কারণ let বাইন্ডিংগুলিতে অ্যাক্সেস ছাড়াই এর সমস্ত বাইন্ডিংগুলি মূল্যায়ন করে যেমন সেগুলি নির্মিত হচ্ছে (তার জন্য, আমাদের প্রয়োজন হবে let*)।

(let ((wolf 5)   ; new binding  <-------------+
      (x wolf))  ; some other variable; free! |
  wolf)          ; bound to ------------------+

সুতরাং এর অর্থ অবশ্যই এটি:

  • আমাদের মূলটি ব্যবহার করে সমস্ত বাইন্ডিং রূপান্তর করতে হবে bound এবং freeতাহলে
  • শুধুমাত্র লেট বডিটির জন্য, নতুন বাইন্ডিংগুলি যুক্ত করুন (এবং মূলটি ব্যবহার করুন free)

যা আমাদের দেয়, কোডে:

class LambdaConverter:
    # ...
    def convert(self, expr, bound, free):
        match expr:
            # ...
            case ("let", bindings, body):
                new_bindings = ()
                for name, val_expr in bindings:
                    new_bindings.append((name, self.convert(val_expr, bound, free)))
                names = name for name, _ in bindings
                new_body = self.convert(body, bound | names, free)
                return ("let", new_bindings, new_body)
            # ...

class LambdaTests(unittest.TestCase):
    # ...
    def test_let(self):
        self.assertEqual(lift_lambdas(("let", (("x", 5)), "x")),
                         ("labels", (), ("let", (("x", 5)), "x")))

    def test_let_lambda(self):
        self.assertEqual(lift_lambdas(("let", (("x", 5)),
                                       ("lambda", ("y"),
                                        ("+", "x", "y")))),
                         ("labels",
                          (("f0", ("code", ("y"), ("x"), ("+", "x", "y")))),
                          ("let", (("x", 5)), ("closure", "f0", "x"))))

    def test_let_inside_lambda(self):
        self.assertEqual(lift_lambdas(("lambda", ("x"),
                                       ("let", (("y", 6)),
                                        ("+", "x", "y")))),
                         ("labels",
                          (("f0", ("code", ("x"), (),
                                   ("let", (("y", 6)),
                                    ("+", "x", "y"))))),
                          ("closure", "f0")))

    def test_paper_example(self):
        self.assertEqual(lift_lambdas(("let", (("x", 5)),
                                         ("lambda", ("y"),
                                          ("lambda", (),
                                           ("+", "x", "y"))))),
                         ("labels", (
                             ("f0", ("code", (),
                               ("x", "y"), ("+", "x", "y"))),
                             ("f1", ("code", ("y"), ("x"),
                               ("closure", "f0", "x", "y"))),
                           ),
                          ("let", (("x", 5)), ("closure", "f1", "x"))))
    # ... and many more, especially interacting with `lambda`

ফাংশন কল

সর্বশেষে এবং কিছুটা উদাসীনভাবে, আমাদের ফাংশন কল রয়েছে। কল করার একমাত্র জিনিসটি আবার এই সর্বদা আবদ্ধ আদিম অপারেটরদের মতো পরিচালনা করছে +যা আমরা একটি করতে চাই না funcall::

class LambdaConverter:
    # ...
    def convert(self, expr, bound, free):
        match expr:
            # ...
            case (func, *args):
                result = () if isinstance(func, str) and func in BUILTINS else ("funcall")
                for e in expr:
                    result.append(self.convert(e, bound, free))
                return result
            # ...

class LambdaTests(unittest.TestCase):
    # ...
    def test_call(self):
        self.assertEqual(lift_lambdas(("f", 3, 4)), ("labels", (), ("funcall", "f", 3, 4)))

এখন আমাদের এই নতুন আছে funcallএবং closure ফর্মগুলি আমাদের সেগুলি সমাবেশে সংকলন করতে হবে।

সংকলন closure

ক্লোজার ফর্মগুলি সংকলন করা একটি স্ট্রিং বা ভেক্টর বরাদ্দের সাথে খুব মিল। প্রথম কক্ষে, আমরা কোডটিতে একটি পয়েন্টার রাখতে চাই যা ক্লোজারটি ব্যাক করে (এটি কিছু লেবেল হবে f12)। আমরা এটি ব্যবহার করে একটি রেফারেন্স পেতে পারি
leaযেহেতু এটি সমাবেশে একটি লেবেল হবে। তারপরে আমরা এটি গাদা লিখি।

তারপরে প্রতিটি ফ্রি ভেরিয়েবলের জন্য, আমরা এটি কোথায় সংজ্ঞায়িত হয়েছে তা সন্ধান করতে যাই। যেহেতু আমরা নির্মাণের মাধ্যমে জানি যে এগুলি সমস্ত স্ট্রিং, তাই চলমান হিপ পয়েন্টারটির ট্র্যাক রাখার আশেপাশে অদ্ভুত পুনরাবৃত্তির সমস্যা থাকার বিষয়ে আমাদের চিন্তা করার দরকার নেই। পরিবর্তে, আমরা জানি এটি সর্বদা স্ট্যাক থেকে বা বর্তমান বন্ধ থেকে পরোক্ষ হতে চলেছে। তারপরে আমরা এটি গাদা লিখি।

তারপরে, যেহেতু একটি বন্ধ একটি বস্তু, তাই আমাদের এটিকে একটি ট্যাগ দেওয়া দরকার। সুতরাং আমরা এটি সঙ্গে ট্যাগ
lea কারণ আমি সুন্দর অনুভব করেছি। আপনিও ব্যবহার করতে পারেন or বা add। আমরা ফলাফল সংরক্ষণ করি rax কারণ এটি আমাদের সংকলক চুক্তি।

সর্বশেষে, আমরা বন্ধের আকার দ্বারা স্তূপের পয়েন্টারটি ধাক্কা দিই।

def compile_expr(expr, code, si, env):
    match expr:
        # ...
        case ("closure", str(lvar), *args):
            comment("Get a pointer to the label")
            emit(f"lea rax, lvar")
            emit(f"mov heap_at(0), rax")
            for idx, arg in enumerate(args):
                assert isinstance(arg, str)
                comment(f"Load closure cell #idx")
                # Just a variable lookup; guaranteed not to allocate
                compile_expr(arg, code, si, env)
                emit(f"mov heap_at((idx+1)*WORD_SIZE), rax")
            comment("Tag a closure pointer")
            emit(f"lea rax, heap_at(CLOSURE_TAG)")
            comment("Bump the heap pointer")
            size = align(WORD_SIZE + len(args)*WORD_SIZE)
            emit(f"add HEAP_BASE, size")
        # ...

সুতরাং (lambda (x) x) সংকলন:

.intel_syntax
.global scheme_entry

f0:
mov rax, (rsp-8)
ret

scheme_entry:
# Get a pointer to the label
lea rax, f0
mov (rsi+0), rax
# Tag a closure pointer
lea rax, (rsi+6)
# Bump the heap pointer
add rsi, 16
ret

এবং যদি আমাদের একটি ক্লোজার ভেরিয়েবল থাকে, উদাহরণস্বরূপ (let ((y 5)) (lambda () y))::

.intel_syntax
.global scheme_entry

f0:
mov rax, (rdi+2)
ret

scheme_entry:
# Code for y
mov rax, 20
# Store y on the stack
mov (rsp-8), rax
# Get a pointer to the label
lea rax, f0
mov (rsi+0), rax
# Load closure cell #0
mov rax, (rsp-8)
mov (rsi+8), rax
# Tag a closure pointer
lea rax, (rsi+6)
# Bump the heap pointer
add rsi, 16
ret

পাঠ্য সমাবেশ নির্গত করার একটি নব্বইটি হ’ল আমি খুব সহজেই ইনলাইন মন্তব্যগুলি যুক্ত করতে পারি। এটাই আমার comment ফাংশনটির জন্য: এটি কেবল উপসর্গ একটি #

… অপেক্ষা করুন, ধরে রাখুন, আমরা কেন পড়ছি rdi+2 একটি ক্লোজার ভেরিয়েবলের জন্য? এটা কোন অর্থবোধ করে না, তাই না?

কারণ আমরা যখন বন্ধটি পড়ছি তখন আমরা একটি ট্যাগযুক্ত পয়েন্টার থেকে পড়ছি। যেহেতু আমরা সূচকটি বন্ধের মধ্যে এবং সংকলন-সময়ে ট্যাগটি জানি, তাই আমরা এগুলিকে একটি ঝরঝরে পরোক্ষ মধ্যে ভাঁজ করতে পারি।

def compile_lexpr(lexpr, code):
    match lexpr:
        case ("code", params, freevars, body):
            env = 
            for idx, param in enumerate(params):
                env(param) = stack_at(-(idx+1)*WORD_SIZE)
            # vvvv New for closures vvvv
            for idx, fvar in enumerate(freevars):
                env(fvar) = indirect(CLOSURE_BASE, (idx+1)*WORD_SIZE - CLOSURE_TAG)
            # ^^^^ New for closures ^^^^
            compile_expr(body, code, si=-(len(env)+1)*WORD_SIZE, env=env)
            code.append("ret")
        case _:
            raise NotImplementedError(lexpr)

এখন কিছু ক্লোজার কল করা যাক…!

সংকলন funcall

আমি কোডটি দেখিয়ে শুরু করব labelcall কারণ এটি একটি ভাল স্টেপিং পাথর funcall (চমৎকার কাজ, ডাঃ গুলুম!)।

প্রধান অংশগুলি হ’ল:

  • রিটার্ন ঠিকানার জন্য স্ট্যাকের উপর স্থান সংরক্ষণ করুন
  • স্ট্যাকের উপর আরগগুলি সংকলন করুন
  • স্থানীয়দের উপরে স্ট্যাক পয়েন্টারটি সামঞ্জস্য করা
  • কল
  • স্ট্যাক পয়েন্টারটি ফিরিয়ে আনছে

আমি মনে করি আমার শেষ সংস্করণে (সি সংস্করণ) আমি এটি পুনরাবৃত্তভাবে করেছি কারণ লুপিংটি আমি তৈরি করা ডেটা স্ট্রাকচারগুলির সাথে সি -তে ঝরঝরে করা চ্যালেঞ্জ অনুভব করেছি তবে যেহেতু এটি পাইথন এবং দ্য ওয়াইল্ড ওয়েস্ট, আমরা লুপিং করছি।

def compile_expr(expr, code, si, env):
    match expr:
        # ...
        case ("labelcall", str(label), *args):
            new_si = si - WORD_SIZE  # Save a word for the return address
            for arg in args:
                compile_expr(arg, code, new_si, env)
                emit(f"mov stack_at(new_si), rax")
                new_si -= WORD_SIZE
            # Align to one word before the return address
            si_adjust = abs(si+WORD_SIZE)
            emit(f"sub rsp, si_adjust")
            emit(f"call label")
            emit(f"add rsp, si_adjust")
        # ...

এর অনেক কিছুই ঠিক তেমন বহন করে funcallদু’টি পার্থক্য সহ:

  • রিটার্ন ঠিকানার জন্য স্ট্যাকের উপর স্থান সংরক্ষণ করুন এবং ক্লোজার পয়েন্টার
  • ফাংশন এক্সপ্রেশনটি সংকলন করুন, যা নির্বিচারে জটিল হতে পারে এবং একটি ক্লোজার পয়েন্টার ফলাফল করে
  • বর্তমান ক্লোজার পয়েন্টারটি সংরক্ষণ করুন
  • নতুন ক্লোজার পয়েন্টার সেট আপ করুন
  • নতুন ক্লোজার পয়েন্টার মাধ্যমে কল করুন
  • পুরানো ক্লোজার পয়েন্টারটি পুনরুদ্ধার করুন

আমি মনে করি স্ট্যাক অ্যাডজাস্টমেন্ট ম্যাথটি এখানে পৌঁছানোর জন্য সবচেয়ে বিরক্তিকর জিনিসটি এবং দূরে ছিল। ওহ, এবং এটি কল করার চেষ্টা করার সময় বন্ধটি আনট্যাগ করার কথাও মনে রাখা।

def compile_expr(expr, code, si, env):
    match expr:
        # ...
        case ("funcall", func, *args):
            # Save a word for the return address and the closure pointer
            clo_si = si - WORD_SIZE
            retaddr_si = clo_si - WORD_SIZE
            new_si = retaddr_si
            # Evaluate arguments
            for arg in args:
                compile_expr(arg, code, new_si, env)
                emit(f"mov stack_at(new_si), rax")
                new_si -= WORD_SIZE
            compile_expr(func, code, new_si, env)
            # Save the current closure pointer
            emit(f"mov stack_at(clo_si), CLOSURE_BASE")
            emit(f"mov CLOSURE_BASE, rax")
            # Align to one word before the return address
            si_adjust = abs(si)
            emit(f"sub rsp, si_adjust")
            emit(f"call indirect(CLOSURE_BASE, -CLOSURE_TAG)")
            emit(f"add rsp, si_adjust")
            emit(f"mov CLOSURE_BASE, stack_at(clo_si)")
        # ...

সুতরাং ((lambda (x) x) 3) সংকলন:

.intel_syntax
.global scheme_entry

f0:
mov rax, (rsp-8)
ret

scheme_entry:
# Evaluate arguments
mov rax, 12
mov (rsp-24), rax
# Get a pointer to the label
lea rax, f0
mov (rsi+0), rax
# Tag a closure pointer
lea rax, (rsi+6)
# Bump the heap pointer
add rsi, 16
# Save the current closure pointer
mov (rsp-16), rdi
mov rdi, rax
# Align to one word before the return address
sub rsp, 8
call (rdi-6)
# Restore stack and closure
add rsp, 8
mov rdi, (rsp-16)
ret

300 লাইন সংকলকের জন্য খারাপ নয়!

মোড়ানো আপ

আমি মনে করি আজকের দিনে এটাই আছে। আমরা ক্লোজার, ফ্রি ভেরিয়েবল বিশ্লেষণ এবং অপ্রত্যক্ষ ফাংশন কল পেয়েছি। এটা বেশ ভাল।

শুভ হ্যাকিং!

সামগ্রীর মিনি টেবিল

Source link

মন্তব্য করুন

আপনার ই-মেইল এ্যাড্রেস প্রকাশিত হবে না। * চিহ্নিত বিষয়গুলো আবশ্যক।