#!/usr/bin/env perl
# Transform assembly in order to clean each stack frame for X86_64.

use strict;
use warnings;

$SIG{__WARN__} = sub { die @_ };

# Whether we are still scanning the content of a function:
our $scanproc = 0;

# Save lines of the function:
our $lines = "";

# Size of the stack for this function:
our $size = 0;

# Counter for assigning unique ids to labels:
our $id=0;

sub emit_code {
    my $qsize = $size / 8;
    my $offset = - $size - 8;

    if($size != 0) {
      # This is a crude hack to disable the stack cleaning on the main
      # stack.  It relies on the fact that the main stack is high in
      # the address space and the other stacks are in the heap (lower).
      print("\tmovq \$0x7fff00000000, %r11\n");
      print("\tcmpq %r11, %rsp\n");
      print("\tjae .Lstack_cleaner_done$id\n");

      # Loop over the stack frame quadwords and zero it:
      print("\tmovabsq \$$qsize, %r11\n");
      print(".Lstack_cleaner_loop$id:\n");
      print("\tmovq    \$0, $offset(%rsp,%r11,8)\n");
      print("\tsubq    \$1, %r11\n");
      print("\tjne     .Lstack_cleaner_loop$id\n");
      print(".Lstack_cleaner_done$id:\n");
    }

    print $lines;

    $id = $id + 1;
    $size = 0;
    $lines = "";
    $scanproc = 0;
}

while (<>) {
  if ($scanproc) {
      $lines = $lines . $_;
      if (m/^[ \t]*.cfi_endproc$/) {
        emit_code();
      } elsif (m/^[ \t]*pushq/) {
         $size += 8;
      } elsif (m/^[ \t]*subq[\t *]\$([0-9]*),[ \t]*%rsp$/) {
         my $val = $1;
         $val = oct($val) if $val =~ /^0/;
         $size += $val;
         emit_code();
      }
  } elsif (m/^[ \t]*.cfi_startproc$/) {
      print $_;

      $scanproc = 1;
  } else {
      print $_;
  }
}
