void set_page_range_attr(unsigned long *pgd, unsigned long virt_addr,
                         unsigned long len, int attr) {
  unsigned long va;
  unsigned long *pgt;
  for (va = virt_addr / 4096; va < (virt_addr + len + 4096 - 1) / 4096; va++) {
    pgt = (unsigned long *)(pgd[va / 1024] & 0xfffffffffffff000);
    pgt[va % 1024] = (pgt[va % 1024] & 0xfffffff8) | attr;
  }
}


x86 assembly (clang, -O0):
<set_page_range_attr>:
push   %rbp
mov    %rsp,%rbp
mov    %rdi,-0x8(%rbp)
mov    %rsi,-0x10(%rbp)
mov    %rdx,-0x18(%rbp)
mov    %ecx,-0x1c(%rbp)
mov    -0x10(%rbp),%rax
shr    $0xc,%rax
mov    %rax,-0x28(%rbp)
mov    -0x28(%rbp),%rax
mov    -0x10(%rbp),%rcx
add    -0x18(%rbp),%rcx
add    $0x1000,%rcx
sub    $0x1,%rcx
shr    $0xc,%rcx
cmp    %rcx,%rax
jae    11a5 <set_page_range_attr+0xa5>
mov    -0x8(%rbp),%rax
mov    -0x28(%rbp),%rcx
shr    $0xa,%rcx
mov    (%rax,%rcx,8),%rax
and    $0xfffffffffffff000,%rax
mov    %rax,-0x30(%rbp)
mov    -0x30(%rbp),%rax
mov    -0x28(%rbp),%rcx
and    $0x3ff,%rcx
movabs $0xfffffff8,%rdx
and    (%rax,%rcx,8),%rdx
movslq -0x1c(%rbp),%rax
or     %rax,%rdx
mov    -0x30(%rbp),%rax
mov    -0x28(%rbp),%rcx
and    $0x3ff,%rcx
mov    %rdx,(%rax,%rcx,8)
mov    -0x28(%rbp),%rax
add    $0x1,%rax
mov    %rax,-0x28(%rbp)
jmp    111f <set_page_range_attr+0x1f>
pop    %rbp
ret

x86 assembly (clang, -O2):
<set_page_range_attr>:
mov    %rsi,%r10
shr    $0xc,%r10
lea    (%rsi,%rdx,1),%r11
add    $0xfff,%r11
shr    $0xc,%r11
cmp    %r10,%r11
jbe    11c3 <set_page_range_attr+0xc3>
movslq %ecx,%rcx
mov    %r11d,%r9d
sub    %r10d,%r9d
lea    0x1(%r10),%r8
test   $0x1,%r9b
je     1159 <set_page_range_attr+0x59>
shr    $0x16,%rsi
mov    $0xfffffffffffff000,%r9
and    (%rdi,%rsi,8),%r9
and    $0x3ff,%r10d
mov    (%r9,%r10,8),%rsi
and    $0xfffffff8,%esi
or     %rcx,%rsi
mov    %rsi,(%r9,%r10,8)
mov    %r8,%r10
cmp    %r8,%r11
je     11c3 <set_page_range_attr+0xc3>
mov    $0xfffffffffffff000,%r8
cs nopw 0x0(%rax,%rax,1)
nop
mov    %r10,%rax
shr    $0xa,%rax
mov    (%rdi,%rax,8),%rax
and    %r8,%rax
mov    %r10d,%edx
and    $0x3ff,%edx
mov    (%rax,%rdx,8),%rsi
and    $0xfffffff8,%esi
or     %rcx,%rsi
mov    %rsi,(%rax,%rdx,8)
lea    0x1(%r10),%rax
mov    %rax,%rdx
shr    $0xa,%rdx
mov    (%rdi,%rdx,8),%rdx
and    %r8,%rdx
and    $0x3ff,%eax
mov    (%rdx,%rax,8),%rsi
and    $0xfffffff8,%esi
or     %rcx,%rsi
mov    %rsi,(%rdx,%rax,8)
add    $0x2,%r10
cmp    %r10,%r11
jne    1170 <set_page_range_attr+0x70>
ret

LLVM IR (-O0):
define void @set_page_range_attr(i64* %0, i64 %1, i64 %2, i32 %3) {
%5 = alloca i64*
%6 = alloca i64
%7 = alloca i64
%8 = alloca i32
%9 = alloca i64
%10 = alloca i64*
store i64* %0, i64** %5
store i64 %1, i64* %6
store i64 %2, i64* %7
store i32 %3, i32* %8
%11 = load i64, i64* %6
%12 = udiv i64 %11, 4096
store i64 %12, i64* %9
br label %13
13: ; preds = %43, %4
%14 = load i64, i64* %9
%15 = load i64, i64* %6
%16 = load i64, i64* %7
%17 = add i64 %15, %16
%18 = add i64 %17, 4096
%19 = sub i64 %18, 1
%20 = udiv i64 %19, 4096
%21 = icmp ult i64 %14, %20
br i1 %21, label %22, label %46
22: ; preds = %13
%23 = load i64*, i64** %5
%24 = load i64, i64* %9
%25 = udiv i64 %24, 1024
%26 = getelementptr inbounds i64, i64* %23, i64 %25
%27 = load i64, i64* %26
%28 = and i64 %27, -4096
%29 = inttoptr i64 %28 to i64*
store i64* %29, i64** %10
%30 = load i64*, i64** %10
%31 = load i64, i64* %9
%32 = urem i64 %31, 1024
%33 = getelementptr inbounds i64, i64* %30, i64 %32
%34 = load i64, i64* %33
%35 = and i64 %34, 4294967288
%36 = load i32, i32* %8
%37 = sext i32 %36 to i64
%38 = or i64 %35, %37
%39 = load i64*, i64** %10
%40 = load i64, i64* %9
%41 = urem i64 %40, 1024
%42 = getelementptr inbounds i64, i64* %39, i64 %41
store i64 %38, i64* %42
br label %43
43: ; preds = %22
%44 = load i64, i64* %9
%45 = add i64 %44, 1
store i64 %45, i64* %9
br label %13
46: ; preds = %13
ret void
}

LLVM IR (-O2):
define void @set_page_range_attr(i64* nocapture %0, i64 %1, i64 %2, i32 %3) {
%5 = lshr i64 %1, 12
%6 = add i64 %1, 4095
%7 = add i64 %6, %2
%8 = lshr i64 %7, 12
%9 = icmp ugt i64 %8, %5
br i1 %9, label %10, label %56
10: ; preds = %4
%11 = sext i32 %3 to i64
%12 = sub nsw i64 %8, %5
%13 = add nuw nsw i64 %5, 1
%14 = and i64 %12, 1
%15 = icmp eq i64 %14, 0
br i1 %15, label %28, label %16
16: ; preds = %10
%17 = lshr i64 %1, 22
%18 = getelementptr inbounds i64, i64* %0, i64 %17
%19 = load i64, i64* %18
%20 = and i64 %19, -4096
%21 = inttoptr i64 %20 to i64*
%22 = and i64 %5, 1023
%23 = getelementptr inbounds i64, i64* %21, i64 %22
%24 = load i64, i64* %23
%25 = and i64 %24, 4294967288
%26 = or i64 %25, %11
store i64 %26, i64* %23
%27 = add nuw nsw i64 %5, 1
br label %28
28: ; preds = %16, %10
%29 = phi i64 [ %5, %10 ], [ %27, %16 ]
%30 = icmp eq i64 %8, %13
br i1 %30, label %56, label %31
31: ; preds = %28, %31
%32 = phi i64 [ %54, %31 ], [ %29, %28 ]
%33 = lshr i64 %32, 10
%34 = getelementptr inbounds i64, i64* %0, i64 %33
%35 = load i64, i64* %34
%36 = and i64 %35, -4096
%37 = inttoptr i64 %36 to i64*
%38 = and i64 %32, 1023
%39 = getelementptr inbounds i64, i64* %37, i64 %38
%40 = load i64, i64* %39
%41 = and i64 %40, 4294967288
%42 = or i64 %41, %11
store i64 %42, i64* %39
%43 = add nuw nsw i64 %32, 1
%44 = lshr i64 %43, 10
%45 = getelementptr inbounds i64, i64* %0, i64 %44
%46 = load i64, i64* %45
%47 = and i64 %46, -4096
%48 = inttoptr i64 %47 to i64*
%49 = and i64 %43, 1023
%50 = getelementptr inbounds i64, i64* %48, i64 %49
%51 = load i64, i64* %50
%52 = and i64 %51, 4294967288
%53 = or i64 %52, %11
store i64 %53, i64* %50
%54 = add nuw nsw i64 %32, 2
%55 = icmp eq i64 %54, %8
br i1 %55, label %56, label %31
56: ; preds = %28, %31, %4
ret void
}