#define NULL ((void *)0)
typedef unsigned long size_t; // Customize by platform.
typedef long intptr_t;
typedef unsigned long uintptr_t;
typedef long scalar_t__; // Either arithmetic or pointer type.
/* By default, we understand bool (as a convenience). */
typedef int bool;
#define false 0
#define true 1

/* Forward declarations */

/* Type definitions */
typedef int uint64_t;

/* Variables and functions */
int gf2_mat_mult16_1(int const, int const *);
uint64_t prince_m_prime_layer(const uint64_t m_prime_in) {
  static const uint64_t m16[2][16] = {
      {0x0111, 0x2220, 0x4404, 0x8088, 0x1011, 0x0222, 0x4440, 0x8808, 0x1101,
       0x2022, 0x0444, 0x8880, 0x1110, 0x2202, 0x4044, 0x0888},
      {0x1110, 0x2202, 0x4044, 0x0888, 0x0111, 0x2220, 0x4404, 0x8088, 0x1011,
       0x0222, 0x4440, 0x8808, 0x1101, 0x2022, 0x0444, 0x8880}};
  const uint64_t chunk0 = gf2_mat_mult16_1(m_prime_in >> (0 * 16), m16[0]);
  const uint64_t chunk1 = gf2_mat_mult16_1(m_prime_in >> (1 * 16), m16[1]);
  const uint64_t chunk2 = gf2_mat_mult16_1(m_prime_in >> (2 * 16), m16[1]);
  const uint64_t chunk3 = gf2_mat_mult16_1(m_prime_in >> (3 * 16), m16[0]);
  const uint64_t m_prime_out = (chunk3 << (3 * 16)) | (chunk2 << (2 * 16)) |
                               (chunk1 << (1 * 16)) | (chunk0 << (0 * 16));
  return m_prime_out;
}


x86 assembly (clang, -O0):
<prince_m_prime_layer>:
push   %rbp
mov    %rsp,%rbp
sub    $0x20,%rsp
mov    %edi,-0x4(%rbp)
mov    -0x4(%rbp),%edi
sar    $0x0,%edi
lea    0xed8(%rip),%rsi
call   1030 <gf2_mat_mult16_1@plt>
mov    %eax,-0x8(%rbp)
mov    -0x4(%rbp),%edi
sar    $0x10,%edi
lea    0xec3(%rip),%rsi
add    $0x40,%rsi
call   1030 <gf2_mat_mult16_1@plt>
mov    %eax,-0xc(%rbp)
mov    -0x4(%rbp),%edi
mov    $0x20,%ecx
sar    %cl,%edi
lea    0xea6(%rip),%rsi
add    $0x40,%rsi
call   1030 <gf2_mat_mult16_1@plt>
mov    %eax,-0x10(%rbp)
mov    -0x4(%rbp),%edi
mov    $0x30,%ecx
sar    %cl,%edi
lea    0xe89(%rip),%rsi
call   1030 <gf2_mat_mult16_1@plt>
mov    %eax,-0x14(%rbp)
mov    -0x14(%rbp),%eax
mov    $0x30,%ecx
shl    %cl,%eax
mov    -0x10(%rbp),%edx
mov    $0x20,%ecx
shl    %cl,%edx
mov    %edx,%ecx
or     %ecx,%eax
mov    -0xc(%rbp),%ecx
shl    $0x10,%ecx
or     %ecx,%eax
mov    -0x8(%rbp),%ecx
shl    $0x0,%ecx
or     %ecx,%eax
mov    %eax,-0x18(%rbp)
mov    -0x18(%rbp),%eax
add    $0x20,%rsp
pop    %rbp
ret

x86 assembly (clang, -O2):
<prince_m_prime_layer>:
push   %r15
push   %r14
push   %rbx
mov    %edi,%ebx
lea    0xee2(%rip),%r14
mov    %r14,%rsi
call   1030 <gf2_mat_mult16_1@plt>
sar    $0x10,%ebx
lea    0xf10(%rip),%r15
mov    %ebx,%edi
mov    %r15,%rsi
call   1030 <gf2_mat_mult16_1@plt>
mov    %r15,%rsi
call   1030 <gf2_mat_mult16_1@plt>
mov    %r14,%rsi
pop    %rbx
pop    %r14
pop    %r15
jmp    1030 <gf2_mat_mult16_1@plt>

LLVM IR (-O0):
@prince_m_prime_layer.m16 = internal constant [2 x [16 x i32]] [[16 x i32] [i32 273, i32 8736, i32 17412, i32 32904, i32 4113, i32 546, i32 17472, i32 34824, i32 4353, i32 8226, i32 1092, i32 34944, i32 4368, i32 8706, i32 16452, i32 2184], [16 x i32] [i32 4368, i32 8706, i32 16452, i32 2184, i32 273, i32 8736, i32 17412, i32 32904, i32 4113, i32 546, i32 17472, i32 34824, i32 4353, i32 8226, i32 1092, i32 34944]]
define i32 @prince_m_prime_layer(i32 %0) {
%2 = alloca i32
%3 = alloca i32
%4 = alloca i32
%5 = alloca i32
%6 = alloca i32
%7 = alloca i32
store i32 %0, i32* %2
%8 = load i32, i32* %2
%9 = ashr i32 %8, 0
%10 = call i32 @gf2_mat_mult16_1(i32 %9, i32* getelementptr inbounds ([2 x [16 x i32]], [2 x [16 x i32]]* @prince_m_prime_layer.m16, i64 0, i64 0, i64 0))
store i32 %10, i32* %3
%11 = load i32, i32* %2
%12 = ashr i32 %11, 16
%13 = call i32 @gf2_mat_mult16_1(i32 %12, i32* getelementptr inbounds ([2 x [16 x i32]], [2 x [16 x i32]]* @prince_m_prime_layer.m16, i64 0, i64 1, i64 0))
store i32 %13, i32* %4
%14 = load i32, i32* %2
%15 = ashr i32 %14, 32
%16 = call i32 @gf2_mat_mult16_1(i32 %15, i32* getelementptr inbounds ([2 x [16 x i32]], [2 x [16 x i32]]* @prince_m_prime_layer.m16, i64 0, i64 1, i64 0))
store i32 %16, i32* %5
%17 = load i32, i32* %2
%18 = ashr i32 %17, 48
%19 = call i32 @gf2_mat_mult16_1(i32 %18, i32* getelementptr inbounds ([2 x [16 x i32]], [2 x [16 x i32]]* @prince_m_prime_layer.m16, i64 0, i64 0, i64 0))
store i32 %19, i32* %6
%20 = load i32, i32* %6
%21 = shl i32 %20, 48
%22 = load i32, i32* %5
%23 = shl i32 %22, 32
%24 = or i32 %21, %23
%25 = load i32, i32* %4
%26 = shl i32 %25, 16
%27 = or i32 %24, %26
%28 = load i32, i32* %3
%29 = shl i32 %28, 0
%30 = or i32 %27, %29
store i32 %30, i32* %7
%31 = load i32, i32* %7
ret i32 %31
}

LLVM IR (-O2):
@prince_m_prime_layer.m16 = internal constant [2 x [16 x i32]] [[16 x i32] [i32 273, i32 8736, i32 17412, i32 32904, i32 4113, i32 546, i32 17472, i32 34824, i32 4353, i32 8226, i32 1092, i32 34944, i32 4368, i32 8706, i32 16452, i32 2184], [16 x i32] [i32 4368, i32 8706, i32 16452, i32 2184, i32 273, i32 8736, i32 17412, i32 32904, i32 4113, i32 546, i32 17472, i32 34824, i32 4353, i32 8226, i32 1092, i32 34944]]
define i32 @prince_m_prime_layer(i32 %0) {
%2 = tail call i32 @gf2_mat_mult16_1(i32 %0, i32* getelementptr inbounds ([2 x [16 x i32]], [2 x [16 x i32]]* @prince_m_prime_layer.m16, i64 0, i64 0, i64 0))
%3 = ashr i32 %0, 16
%4 = tail call i32 @gf2_mat_mult16_1(i32 %3, i32* getelementptr inbounds ([2 x [16 x i32]], [2 x [16 x i32]]* @prince_m_prime_layer.m16, i64 0, i64 1, i64 0))
%5 = tail call i32 @gf2_mat_mult16_1(i32 poison, i32* getelementptr inbounds ([2 x [16 x i32]], [2 x [16 x i32]]* @prince_m_prime_layer.m16, i64 0, i64 1, i64 0))
%6 = tail call i32 @gf2_mat_mult16_1(i32 poison, i32* getelementptr inbounds ([2 x [16 x i32]], [2 x [16 x i32]]* @prince_m_prime_layer.m16, i64 0, i64 0, i64 0))
ret i32 poison
}