<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Denis Sedchenko</title>
    <description>The latest articles on DEV Community by Denis Sedchenko (@x1unix).</description>
    <link>https://dev.to/x1unix</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F173739%2Ffd8e6c47-7322-449c-9eca-3e09762afd0f.jpg</url>
      <title>DEV Community: Denis Sedchenko</title>
      <link>https://dev.to/x1unix</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/x1unix"/>
    <language>en</language>
    <item>
      <title>ArchLinux Setup Guide For Intel MacBook Pro</title>
      <dc:creator>Denis Sedchenko</dc:creator>
      <pubDate>Fri, 02 May 2025 04:53:31 +0000</pubDate>
      <link>https://dev.to/x1unix/archlinux-setup-guide-for-intel-macbook-pro-58b8</link>
      <guid>https://dev.to/x1unix/archlinux-setup-guide-for-intel-macbook-pro-58b8</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;This article is a collection of caveats necessary to get Arch Linux up and running on old Intel MacBooks with AMD GPUs.&lt;/p&gt;

&lt;p&gt;In my case, it's old &lt;code&gt;MacBookPro11,5&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Steps here are based on various articles and hours of painful debugging.&lt;/p&gt;

&lt;p&gt;Article skips full Arch setup process, focusing only on MacBook-specific steps.&lt;/p&gt;

&lt;p&gt;This article assumes that you’re using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PipeWire as audio server.&lt;/li&gt;
&lt;li&gt;Wayland with Hyprland as a compositor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;Information in this article is based on following sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wiki.t2linux.org/guides/hybrid-graphics/" rel="noopener noreferrer"&gt;https://wiki.t2linux.org/guides/hybrid-graphics/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Dunedan/mbp-2016-linux" rel="noopener noreferrer"&gt;https://github.com/Dunedan/mbp-2016-linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.archlinux.org/title/MacBookPro11,x" rel="noopener noreferrer"&gt;https://wiki.archlinux.org/title/MacBookPro11,x&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pandeiro/arch-on-air" rel="noopener noreferrer"&gt;https://github.com/pandeiro/arch-on-air&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Bootloader

&lt;ul&gt;
&lt;li&gt;rEFInd Installation&lt;/li&gt;
&lt;li&gt;Set Apple OS&lt;/li&gt;
&lt;li&gt;Extra Boot Arguments&lt;/li&gt;
&lt;li&gt;Setting rEFInd as default bootloader&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Network

&lt;ul&gt;
&lt;li&gt;Network Manager&lt;/li&gt;
&lt;li&gt;Broadcom&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

GPU Power Management

&lt;ul&gt;
&lt;li&gt;Udev Rules&lt;/li&gt;
&lt;li&gt;Kernel Modules&lt;/li&gt;
&lt;li&gt;Use Integrated GPU On Boot Using gpu-switch&lt;/li&gt;
&lt;li&gt;Disable AMDGPU HDMI Audio&lt;/li&gt;
&lt;li&gt;Turn Off Discrete AMD GPU&lt;/li&gt;
&lt;li&gt;Disable dGPU On Boot&lt;/li&gt;
&lt;li&gt;Use iGPU for Hyprland&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Power Efficiency

&lt;ul&gt;
&lt;li&gt;Battery Monitoring&lt;/li&gt;
&lt;li&gt;iGPU Tweaks&lt;/li&gt;
&lt;li&gt;Pefrormance Power Profile&lt;/li&gt;
&lt;li&gt;Automatic Power Profiles&lt;/li&gt;
&lt;li&gt;Switch Power Profiles Manually&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bootloader
&lt;/h2&gt;

&lt;p&gt;Apple introduces certain limitations for non-macOS systems, such as inability to use Intel GPU.&lt;/p&gt;

&lt;p&gt;The most convenient way to bypass them is using &lt;strong&gt;rEFInd&lt;/strong&gt; bootloader.&lt;br&gt;
rEFInd handles MacOS version spoofing, SIP and other stuff out of the box.&lt;/p&gt;
&lt;h3&gt;
  
  
  rEFInd Installation
&lt;/h3&gt;

&lt;p&gt;Install rEFInd by following instructions from &lt;a href="https://wiki.archlinux.org/title/REFInd" rel="noopener noreferrer"&gt;ArchWiki&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;⚠️ &lt;strong&gt;Important&lt;/strong&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Don’t forget to add Linux boot configuration into rEFInd config file &lt;code&gt;/boot/EFI/refind/refind.conf&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Set Apple OS
&lt;/h3&gt;

&lt;p&gt;Apple blocks access to integrated Intel GPU for any non-macOS operating system.&lt;/p&gt;

&lt;p&gt;rEFInd provides a way to workaround this with &lt;code&gt;spoof_osx_version&lt;/code&gt; config parameter.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;/boot/EFI/refind/refind.conf&lt;/code&gt; and add a following parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spoof_osx_version 12.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extra Boot Arguments
&lt;/h3&gt;

&lt;p&gt;Add following options to your Arch Linux menu entry in rEFInd config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data=writeback libata.force=1:noncq acpi_mask_gpe=0x06 acpi_osi=Darwin i915.modeset=1 amdgpu.modeset=0 amdgpu.runpm=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting rEFInd as default bootloader
&lt;/h3&gt;

&lt;p&gt;Unlike Grub or systemd-boot, rEFInd places itself into a non-standard directory inside EFI partition.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;efibootmgr&lt;/code&gt; to add rEFInd as boot entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;efibootmgr &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="s1"&gt;'rEFInd Boot Manager'&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="s1"&gt;'\EFI\refind\refind_x64.efi'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;⚠️ &lt;strong&gt;Important&lt;/strong&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Remove old macOS boot entries using &lt;code&gt;efibootmgr&lt;/code&gt; to avoid having long boot delays.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Network
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Network Manager
&lt;/h3&gt;

&lt;p&gt;NetworkManager won’t let you to connect to a WiFI without a proper dbus session and keychain provider.&lt;/p&gt;

&lt;p&gt;The simplest and most reliable way is to use &lt;code&gt;iwd&lt;/code&gt; instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; iwd
systemctl disable &lt;span class="nt"&gt;--now&lt;/span&gt; NetworkManager
systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; systemd-resolved
systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; systemd-networkd
systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; iwd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;💡 &lt;strong&gt;Tip&lt;/strong&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Check &lt;a href="https://wiki.archlinux.org/title/Iwd" rel="noopener noreferrer"&gt;this ArchWiki page&lt;/a&gt; for instructions how to connect to a WiFi network.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Broadcom
&lt;/h3&gt;

&lt;p&gt;You might encounter issues related to missing Broadcom firmware.&lt;/p&gt;

&lt;p&gt;In that case, installing &lt;code&gt;broadcom-wl&lt;/code&gt; might help:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;paru broadcom-wl-dmks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, create &lt;code&gt;/etc/modprobe.d/broadcom-wl-dkms.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;blacklist brcm80211
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  GPU Power Management
&lt;/h2&gt;

&lt;p&gt;Unfortunately AMD Cape-Verde gGPUs &lt;strong&gt;don’t support&lt;/strong&gt; dynamic power management. This means, dGPU has to be shut down manually in order to save battery.&lt;/p&gt;

&lt;h3&gt;
  
  
  Udev Rules
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;/etc/udev/rules.d/30-amdgpu-pm.rules&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ACTION=="add", SUBSYSTEM=="drm", DRIVERS=="amdgpu", ATTR{device/power/control}="auto"
ACTION=="add", SUBSYSTEM=="drm", DRIVERS=="amdgpu", ATTR{device/power_dpm_force_performance_level}="low"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Kernel Modules
&lt;/h3&gt;

&lt;p&gt;Go to &lt;code&gt;/etc/modprobe.d&lt;/code&gt; and create a couple of files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;amdgpu.conf&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;options amdgpu modeset=0
options amdgpu enable_psr=1
options amdgpu si_support=1
options amdgpu cik_support=1
options amdgpu runpm=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;radeon.conf&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;options radeon si_support=0
options radeon cik_support=0
options radeon runpm=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run &lt;code&gt;sudo mkinitcpio -P&lt;/code&gt; to apply changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Integrated GPU On Boot Using &lt;a href="https://github.com/0xbb/gpu-switch" rel="noopener noreferrer"&gt;gpu-switch&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Install &lt;code&gt;gpu-switch&lt;/code&gt; tool from AUR using Paru.&lt;/p&gt;

&lt;p&gt;Then, use a following command to set iGPU as a primary GPU to drive display:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;gpu-switch &lt;span class="nt"&gt;-i&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ℹ️ &lt;strong&gt;Note&lt;/strong&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Operation takes affect only after reboot.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Disable AMDGPU HDMI Audio
&lt;/h3&gt;

&lt;p&gt;WirePlumber is using &lt;code&gt;/dev/snd/controlC2&lt;/code&gt; which is AMD GPU's HDMI Audio.&lt;/p&gt;

&lt;p&gt;This prevents AMDGPU to put a device into D3 cold state.&lt;/p&gt;

&lt;p&gt;Make &lt;code&gt;~/.config/wireplumber/wireplumber.conf.d/50-disable-dgpu-hdmi.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# disable the HDA function on the Radeon R9 M370X (PCI 01:00.1)
&lt;/span&gt;&lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alsa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;device.name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alsa_card.pci-0000_01_00.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;restart wireplumber: &lt;code&gt;systemctl --user restart wireplumber&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;check if &lt;code&gt;DIS-Audio&lt;/code&gt; is &lt;code&gt;DynOff&lt;/code&gt;: &lt;code&gt;sudo cat /sys/kernel/debug/vgaswitcheroo/switch&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Turn Off Discrete AMD GPU
&lt;/h3&gt;

&lt;p&gt;Both GPUs are controlled using Apple's GMUX. It is responsible for turning on and off discrete AMD GPU.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.kernel.org/gpu/vga-switcheroo.html" rel="noopener noreferrer"&gt;vga-switcheroo&lt;/a&gt; kernel module is responsible for controlling a mux.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# set to integrated&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo IDG &amp;gt; /sys/kernel/debug/vgaswitcheroo/switch'&lt;/span&gt;

&lt;span class="c"&gt;# turn off dGPU&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo OFF &amp;gt; /sys/kernel/debug/vgaswitcheroo/switch'&lt;/span&gt;

&lt;span class="c"&gt;# print mux state.&lt;/span&gt;
&lt;span class="c"&gt;# ensure that "DIS" is "OFF" and "DIS-Audio" is "DynOff"&lt;/span&gt;
&lt;span class="nb"&gt;sudo cat&lt;/span&gt; /sys/kernel/debug/vgaswitcheroo/switch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output of &lt;code&gt;/sys/kernel/vgaswitcheroo/switch&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0:IGD:+:Pwr:0000:00:02.0
1:DIS: :Off:0000:01:00.0
2:DIS-Audio: :DynOff:0000:01:00.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Disable dGPU On Boot
&lt;/h3&gt;

&lt;p&gt;Create oneshot systemd unit file &lt;code&gt;/etc/systemd/system/dgpu-poweroff.service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Disable discrete GPU (vgaswitcheroo OFF)&lt;/span&gt;
&lt;span class="py"&gt;Requires&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sys-kernel-debug.mount&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sys-kernel-debug.mount&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;oneshot&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/sh -c 'echo OFF &amp;gt; /sys/kernel/debug/vgaswitcheroo/switch'&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target suspend.target   # run at boot *and* after resume&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, start the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; dgpu-poweroff.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use iGPU for Hyprland
&lt;/h3&gt;

&lt;p&gt;Find GPU PCI of integrated GPU:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lspci | &lt;span class="nb"&gt;grep &lt;/span&gt;VGA
00:02.0 VGA compatible controller: Intel Corporation Crystal Well Integrated Graphics Controller &lt;span class="o"&gt;(&lt;/span&gt;rev 08&lt;span class="o"&gt;)&lt;/span&gt;
01:00.0 VGA compatible controller: Advanced Micro Devices, Inc. &lt;span class="o"&gt;[&lt;/span&gt;AMD/ATI] Venus XT &lt;span class="o"&gt;[&lt;/span&gt;Radeon HD 8870M / R9 M270X/M370X] &lt;span class="o"&gt;(&lt;/span&gt;rev 83&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check what DRI card maps to PCI ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /dev/dri/by-path
total 0
lrwxrwxrwx 1 root root  8 Apr 23 03:14 pci-0000:00:02.0-card -&amp;gt; ../card1
lrwxrwxrwx 1 root root 13 Apr 23 03:14 pci-0000:00:02.0-render -&amp;gt; ../renderD128
lrwxrwxrwx 1 root root  8 Apr 23 03:14 pci-0000:01:00.0-card -&amp;gt; ../card0
lrwxrwxrwx 1 root root 13 Apr 23 03:14 pci-0000:01:00.0-render -&amp;gt; ../renderD129
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;In my case, &lt;code&gt;card1&lt;/code&gt; is Intel and &lt;code&gt;card0&lt;/code&gt; is AMD.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To make Intel GPU a preferred GPU for Hyprland, add following into a &lt;code&gt;~/.config/hypr/hyprland.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# Prefer iGPU
&lt;/span&gt;&lt;span class="py"&gt;env&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;AQ_DRM_DEVICES,/dev/dri/card1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Power Efficiency
&lt;/h2&gt;

&lt;p&gt;Even with disabled AMD GPU, MacBook still consumes about 15–20 watts (which is a lot).&lt;/p&gt;

&lt;p&gt;Although there are a lot of things that can be tweaked, the most basic thing to address is setting a correct CPU power profile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Battery Monitoring
&lt;/h3&gt;

&lt;p&gt;I recommend using &lt;a href="https://github.com/svartalf/rust-battop" rel="noopener noreferrer"&gt;battop&lt;/a&gt; tool to monitor power consumption during power optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  iGPU Tweaks
&lt;/h3&gt;

&lt;p&gt;PSR (panel self-refresh) and FBC (frame-buffer compression) save ~0.3-0.5 W when the screen shows static content.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;/etc/modprobe.d/i915.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;options i915 enable_psr=1
options i915 enable_fbc=1
options i915 enable_dc=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pefrormance Power Profile
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Automatic Power Profiles
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;auto-cpufreq&lt;/code&gt; daemon allows automatic CPU power profile configuration based on battery capacity and charging status.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic power profile management based on charging state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By default it will put CPU into a power save mode when not connected to a charger. This will cause a drastic performance decrease.&lt;/li&gt;
&lt;li&gt;Written in Python and consumes 20MB of RAM which is quite a lot for a simple CPU frequency daemon.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install &lt;code&gt;auto-cpufreq&lt;/code&gt; daemon to automatically control CPU frequency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;paru auto-cpufreq
sudo systemctl enable --now auto-cpufreq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;⚠️ &lt;strong&gt;Important&lt;/strong&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Don't install &lt;code&gt;auto-cpufreq-git&lt;/code&gt; as it's broken.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Switch Power Profiles Manually
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;power-profiles-deamon&lt;/code&gt; package provides DBus service and a tool to switch CPU profiles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; power-profiles-daemon
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; power-profiles-daemon.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later, you can change power profile to power saver, performance or balanced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;powerprofilesctl &lt;span class="nb"&gt;set &lt;/span&gt;power-saver
powerprofilesctl &lt;span class="nb"&gt;set &lt;/span&gt;balanced
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>archlinux</category>
      <category>macbook</category>
      <category>linux</category>
    </item>
    <item>
      <title>Building Go packages for Windows on ARM</title>
      <dc:creator>Denis Sedchenko</dc:creator>
      <pubDate>Sun, 16 Jun 2024 21:59:09 +0000</pubDate>
      <link>https://dev.to/x1unix/building-go-packages-for-windows-on-arm-1721</link>
      <guid>https://dev.to/x1unix/building-go-packages-for-windows-on-arm-1721</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Recently, Qualcomm announced &lt;a href="https://www.qualcomm.com/products/mobile/snapdragon/pcs-and-tablets/laptop-device-finder" rel="noopener noreferrer"&gt;list of laptops&lt;/a&gt; that will have their new Snapdragon X Elite chip which claim to have the same performance as Apple's M3 processor.&lt;/p&gt;

&lt;p&gt;This means that in a near future, market will see more and more Windows on ARM laptops and we as developers should be prepared to that.&lt;/p&gt;

&lt;p&gt;This article is structured in Q&amp;amp;A sections to briefly explain the topic.&lt;/p&gt;



&lt;ul&gt;
&lt;li&gt;WoA can emulate x86, isn't it?&lt;/li&gt;
&lt;li&gt;Doesn't Go already support cross compilation?&lt;/li&gt;
&lt;li&gt;Okay, isn't MinGW already cover that?&lt;/li&gt;
&lt;li&gt;So how do I build a program with CGO for WoA?&lt;/li&gt;
&lt;li&gt;Is there a more convenient way to use llvm-mingw?&lt;/li&gt;
&lt;li&gt;
How to use it?

&lt;ul&gt;
&lt;li&gt;Result&lt;/li&gt;
&lt;li&gt;How can I integrate this into my CI?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;



&lt;h2&gt;
  
  
  WoA can emulate x86, isn't it?
&lt;/h2&gt;

&lt;p&gt;Although Windows on ARM (WoA) supports x86 and x86-64 emulation, some features like vector instructions (like AVX2) might be not supported, also emulation brings some performance penalty.&lt;/p&gt;

&lt;p&gt;People who use libraries like &lt;a href="https://github.com/minio/simdjson-go" rel="noopener noreferrer"&gt;simdjson&lt;/a&gt; might be at risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doesn't Go already support cross compilation?
&lt;/h2&gt;

&lt;p&gt;Go offers out-of-box cross compilation support, including ARM64 for Windows, but this doesn't cover CGO.&lt;/p&gt;

&lt;p&gt;That means, any program that uses CGO libraries like &lt;a href="https://github.com/mattn/go-sqlite3" rel="noopener noreferrer"&gt;sqlite3&lt;/a&gt; will require C and/or C++ toolchain for cross-compilation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Okay, isn't MinGW already cover that?
&lt;/h2&gt;

&lt;p&gt;Most popular solution for cross-compilation from Windows and Linux is MinGW.&lt;/p&gt;

&lt;p&gt;Unfortunately, MinGW doesn't support ARM64 target.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.msys2.org/wiki/arm64/" rel="noopener noreferrer"&gt;MSYS2&lt;/a&gt; toolchain provide ARM support but available only for Windows.&lt;/p&gt;

&lt;p&gt;That means that in order to build programs for Windows on ARM on a Linux machine, a different option should be considered as MinGW doesn't support arm64 target.&lt;/p&gt;

&lt;h2&gt;
  
  
  So how do I build a program with CGO for WoA?
&lt;/h2&gt;

&lt;p&gt;Don't worry, this case is already covered.&lt;/p&gt;

&lt;p&gt;There is a llvm-based alternative toolchain that supports WoA - &lt;a href="https://github.com/mstorsjo/llvm-mingw" rel="noopener noreferrer"&gt;llvm-mingw&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately it's not available in most distros except &lt;a href="https://aur.archlinux.org/packages/mingw-w64-llvm" rel="noopener noreferrer"&gt;Arch Linux&lt;/a&gt;, but you still can download prebuilt binaries from releases page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is there a more convenient way to use llvm-mingw?
&lt;/h2&gt;

&lt;p&gt;Yes, sure!&lt;/p&gt;

&lt;p&gt;There is a special &lt;a href="//github.com/x1unix/docker-go-mingw/"&gt;Docker image&lt;/a&gt; with toolchain for all Windows architectures - amd64, x86 and &lt;strong&gt;arm64&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Image is called &lt;code&gt;x1unix/go-mingw&lt;/code&gt; and offers arm64 target support since go &lt;code&gt;1.21&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Image uses MinGW for x86 and llvm-mingw for arm64 target.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use it?
&lt;/h2&gt;

&lt;p&gt;It's quite simple, just pull the image and call &lt;code&gt;go build&lt;/code&gt; command with &lt;code&gt;GOARCH=arm64&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;For example, let's take a simple WinAPI CGO example from &lt;a href="https://github.com/x1unix/docker-go-mingw/tree/master/example/hello" rel="noopener noreferrer"&gt;here&lt;/a&gt; and build it using Docker image:&lt;/p&gt;

&lt;p&gt;
  Go code
  &lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="c"&gt;/*
#cgo LDFLAGS: -lkernel32
#include &amp;lt;windows.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;

// Function to show a MessageBox using WinAPI
void hello() {
  SYSTEM_INFO si;
  ZeroMemory( &amp;amp; si, sizeof(SYSTEM_INFO));
  GetSystemInfo( &amp;amp; si);
  char * arch;
  switch (si.wProcessorArchitecture) {
  case PROCESSOR_ARCHITECTURE_AMD64:
    arch = "AMD64";
    break;
  case PROCESSOR_ARCHITECTURE_INTEL:
    arch = "x86";
    break;
  case PROCESSOR_ARCHITECTURE_ARM:
    arch = "ARM";
    break;
  case PROCESSOR_ARCHITECTURE_ARM64:
    arch = "ARM64";
    break;
  case PROCESSOR_ARCHITECTURE_IA64:
    arch = "IA";
    break;
  default:
    arch = "Unknown";
    break;
  }

  char message[30];
  sprintf(message, "Hello from CGO on %s", arch);

  MessageBox(NULL, message, "Hello World", MB_OK);
}
*/&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Calling C function to open a MessageBox..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="c"&gt;# Go version to use. WoA supported since Go 1.21.&lt;/span&gt;&lt;br&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GO_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.22&lt;/p&gt;

&lt;p&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm64 &lt;span class="se"&gt;&amp;lt;/span&amp;gt;&lt;br&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; .:/go/work &lt;span class="nt"&gt;-w&lt;/span&gt; /go/work &lt;span class="se"&gt;&amp;lt;/span&amp;gt;&lt;br&gt;
    x1unix/go-mingw:&lt;span class="nv"&gt;$GO_VERSION&lt;/span&gt; &lt;span class="se"&gt;&amp;lt;/span&amp;gt;&lt;br&gt;
    go build &lt;span class="nt"&gt;-o&lt;/span&gt; hello.exe &lt;span class="nb"&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Result&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;After building a program, let's try to run it inside any VM with Windows on Arm.&lt;/p&gt;

&lt;p&gt;Parallels Workstation is used for this example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8f7pzmjw3d90ig1a7wk7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8f7pzmjw3d90ig1a7wk7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How can I integrate this into my CI?
&lt;/h3&gt;

&lt;p&gt;As this is a plain Docker image, it can be easily used both in GitHub Actions and Gitlab CI.&lt;/p&gt;

&lt;p&gt;Please check CI templates &lt;a href="https://github.com/x1unix/docker-go-mingw/tree/master/example/ci" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>cgo</category>
      <category>windowsonarm</category>
    </item>
    <item>
      <title>Go WebAssembly Internals - Part 2</title>
      <dc:creator>Denis Sedchenko</dc:creator>
      <pubDate>Fri, 04 Nov 2022 06:52:34 +0000</pubDate>
      <link>https://dev.to/x1unix/go-webassembly-internals-part-2-51am</link>
      <guid>https://dev.to/x1unix/go-webassembly-internals-part-2-51am</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/x1unix/go-webassembly-internals-part-1-14aj"&gt;previous article&lt;/a&gt; we covered how to build a simple Go program, interact with host environment by wrapping Go functions as JavaScript functions and how JS-to-Go call magic works under the hood.&lt;/p&gt;

&lt;p&gt;This article will cover how Go runtime access global JavaScript objects and helper functions from &lt;code&gt;wasm_exec.js&lt;/code&gt; glue-code library and how this mechanism can be exploited to link external JavaScript functions directly to our programs.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebAssembly Import Object
&lt;/h2&gt;

&lt;p&gt;Although WebAssembly programs are isolated and have no direct access to a browser (or other host environment), each module can &lt;a href="https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format#importing_functions_from_javascript"&gt;import&lt;/a&gt; symbols (usually functions) during instantiation that can be used to communicate with outside world.&lt;/p&gt;

&lt;p&gt;All module dependencies to import have to be provided in a form of an object with a symbol name as key and function as a value.&lt;/p&gt;

&lt;p&gt;On WASM program side, module should do an import with &lt;code&gt;import&lt;/code&gt; statement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(module
    (func $myFunction (;0;) (import "myFunction") (param i32))
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browser will link import object to a program during module instantiation process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;importObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myFunction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instantiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* wasm module binary */&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;importObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Go Runtime Dependencies
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1lvyv3bwy0aeo46sjgt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1lvyv3bwy0aeo46sjgt.png" alt="WebAssembly Imports List In" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Go WebAssembly module imports&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each Go program import a couple of runtime dependencies from import object prepared by &lt;code&gt;Go&lt;/code&gt; helper class provided from &lt;a href="https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.js"&gt;&lt;code&gt;wasm_exec.js&lt;/code&gt;&lt;/a&gt; file.&lt;/p&gt;

&lt;p&gt;Most of those functions are used for &lt;code&gt;syscall/js&lt;/code&gt; package but also there are a few core Go runtime dependencies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nw0w5lp4ssuunzm16up.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nw0w5lp4ssuunzm16up.png" alt="Import Object in wasm_exec.js file" width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Import object in &lt;code&gt;wasm_exec.js&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each imported function accepts current program stack pointer and may manipulate with go program memory inside &lt;code&gt;this.mem&lt;/code&gt; field of &lt;code&gt;Go&lt;/code&gt; helper class and return result.&lt;/p&gt;

&lt;p&gt;We also see to what exact Go function is will be linked and what params it will return by function key name and comments left above function declarations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linking Imported Function
&lt;/h2&gt;

&lt;p&gt;Lets have a look at declaration of &lt;a href="https://github.com/golang/go/blob/master/src/syscall/js/js.go#L212"&gt;&lt;code&gt;syscall/js.stringVal&lt;/code&gt;&lt;/a&gt; function which present in import object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//go:build js &amp;amp;&amp;amp; wasm&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt; &lt;span class="kt"&gt;uint64&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;stringVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;stringVal&lt;/code&gt; function doesn't have &lt;code&gt;//go:linkname&lt;/code&gt; or other compiler directives, so how Go linker know that function implementation is defined in import object?&lt;/p&gt;

&lt;p&gt;Function implementation is defined in a separate file &lt;code&gt;js_js.s&lt;/code&gt; in the same folder. File is written in a &lt;a href="https://go.dev/doc/asm"&gt;Go assembler language&lt;/a&gt; and contains body for each &lt;code&gt;syscall/js&lt;/code&gt; dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#include "textflag.h"

TEXT ·stringVal(SB), NOSPLIT, $0
  CallImport
  RET

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;CallImport&lt;/code&gt; instruction is used to declare and call function imports.&lt;br&gt;
Under the hood, Go compiler will generate an &lt;code&gt;import&lt;/code&gt; statement with a path that corresponds to a function (including package name).&lt;/p&gt;

&lt;p&gt;There is a &lt;a href="https://github.com/golang/go/issues/38248"&gt;proposal&lt;/a&gt; to replace &lt;code&gt;CallImport&lt;/code&gt; instruction with a more convenient &lt;code&gt;//go:wasmimport&lt;/code&gt; compiler directive.&lt;/p&gt;
&lt;h2&gt;
  
  
  Importing Custom Functions
&lt;/h2&gt;

&lt;p&gt;All information above allows to define and link custom functions directly to a Go program. Execution of such calls are much cheaper from performance standpoint and Go programs and don't require global namespace pollution (function shouldn't be present in &lt;code&gt;window&lt;/code&gt; object).&lt;/p&gt;

&lt;p&gt;The main downside of this approach is that we have to manually deal with Go program stack, manually read and write data into program memory.&lt;/p&gt;

&lt;p&gt;Lets write and import a simple multiplication function. Function will accept 2 integers and will return a result.&lt;/p&gt;

&lt;p&gt;Full example source code is available in &lt;a href="https://github.com/x1unix/go-wasm-imports-example"&gt;this repo&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Go Program
&lt;/h3&gt;

&lt;p&gt;Our program will consist of 2 files: a main Go file and accompanying Go assembly file with WASM import.&lt;/p&gt;

&lt;p&gt;Program will import and call &lt;code&gt;multiply&lt;/code&gt; function which is written in JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;main.go&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Multiply result:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;main_js.s&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#include "textflag.h"

TEXT ·multiply(SB), NOSPLIT, $0
  CallImport
  RET

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;textflag.h&lt;/code&gt; file is available in &lt;code&gt;$GOROOT/src/runtime&lt;/code&gt; directory.&lt;/p&gt;

&lt;h4&gt;
  
  
  Building Program
&lt;/h4&gt;

&lt;p&gt;Set &lt;code&gt;GOOS&lt;/code&gt; and &lt;code&gt;GOARCH&lt;/code&gt; environment variables to build a program as WebAssembly module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;js &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wasm go build &lt;span class="nt"&gt;-o&lt;/span&gt; main.wasm main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Writing Wrapper
&lt;/h3&gt;

&lt;p&gt;Go SDK provides a &lt;code&gt;wasm_exec.js&lt;/code&gt; file with &lt;code&gt;Go&lt;/code&gt; helper class that implements Go WebAssembly ABI for browsers and contains an import object that needs to be modified.&lt;/p&gt;

&lt;p&gt;Copy of the file is available in &lt;code&gt;$GOROOT/misc/wasm/wasm_exec.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Lets extend &lt;code&gt;Go&lt;/code&gt; class with a small wrapper which will allow exporting custom functions without touching original &lt;code&gt;Go&lt;/code&gt; class implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;custom-go.mjs&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Copied from '$GOROOT/misc/wasm/wasm_exec.js'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./wasm_exec.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomGo&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Go&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;MAX_I32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Copied from 'setInt64'&lt;/span&gt;
  &lt;span class="nf"&gt;setInt64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MAX_I32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Adds function to import object
   * @param name symbol name (package.functionName)
   * @param func function.
   */&lt;/span&gt;
  &lt;span class="nf"&gt;importFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;importObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;func&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Function Implementation
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;main.mjs&lt;/code&gt; file that will import and run our WebAssembly program.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Go&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./custom-go.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Instantiate Go wrapper instance&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Add our function to import object&lt;/span&gt;
&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;importFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;main.multiply&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInt32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// SP + sizeof(int64)  &lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInt32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// SP + sizeof(int64) * 2&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;a2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Got call from Go:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setInt64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


&lt;span class="c1"&gt;// Run the program&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./main.wasm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instantiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;importObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Reading arguments
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;sp&lt;/code&gt; argument is a stack pointer address which is passed to each imported function.&lt;br&gt;
Values of first and second integers should be manually obtained from stack. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Go&lt;/code&gt; class has &lt;code&gt;mem&lt;/code&gt; property which is an instance of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView"&gt;&lt;code&gt;DataView&lt;/code&gt;&lt;/a&gt; interface. As MDN documentation says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;DataView provides a low-level interface for reading and writing different number values into program memory without having to care about platform &lt;a href="[endianness](https://developer.mozilla.org/en-US/docs/Glossary/Endianness)"&gt;endianness&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;this.mem.getInt32&lt;/code&gt; method accepts offset and boolean parameter to indicate that our value is stored in little endian format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInt32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Stack Pointer + sizeof(int64)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInt32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Stack Pointer + sizeof(int64) * 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Returning result
&lt;/h4&gt;

&lt;p&gt;Result of the function should be put in memory after the last argument on stack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setInt64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final Result
&lt;/h3&gt;

&lt;p&gt;Lets run our WebAssembly module with &lt;code&gt;multiply&lt;/code&gt; function implementation inside Node.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attention:&lt;/strong&gt; for Node.js 18 and below, &lt;a href="https://github.com/PeculiarVentures/webcrypto"&gt;WebCrypto API polyfil&lt;/a&gt; is required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;node ./main.mjs

Got call from Go: &lt;span class="o"&gt;{&lt;/span&gt; a1: 3, a2: 4, result: 12 &lt;span class="o"&gt;}&lt;/span&gt;
Multiply result: 12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full example source code with polyfill is available in &lt;a href="https://github.com/x1unix/go-wasm-imports-example"&gt;this repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>webassembly</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Go WebAssembly Internals - Part 1</title>
      <dc:creator>Denis Sedchenko</dc:creator>
      <pubDate>Sat, 29 Oct 2022 17:56:11 +0000</pubDate>
      <link>https://dev.to/x1unix/go-webassembly-internals-part-1-14aj</link>
      <guid>https://dev.to/x1unix/go-webassembly-internals-part-1-14aj</guid>
      <description>&lt;p&gt;In Go 1.11 was introduced WebAssembly support. WebAssembly is a binary executable format that was primary designed to run in web browsers but later started to become popular on other targets such as &lt;a href="https://www.cncf.io/blog/2021/08/25/webassembly-serverless-functions-in-aws-lambda/" rel="noopener noreferrer"&gt;serverless&lt;/a&gt; and even &lt;a href="https://www.docker.com/blog/docker-wasm-technical-preview/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This series or articles will cover some Go internals that used to communicate with host JavaScript environment, its limitations and hacks that can be achieved by exploiting some undocumented features used for Go runtime internal purpose.&lt;/p&gt;

&lt;p&gt;This first article will cover basics of &lt;code&gt;syscall/js&lt;/code&gt; package and how Go code invocation from JS works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; The &lt;a href="https://dev.to/x1unix/go-webassembly-internals-part-2-51am"&gt;next part&lt;/a&gt; of series is already available.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebAssembly Communication Model
&lt;/h2&gt;

&lt;p&gt;By default, WebAssembly doesn't have direct access to host system (JS world in our case), it can't directly access &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model" rel="noopener noreferrer"&gt;DOM&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/ru/docs/Web/API/Window" rel="noopener noreferrer"&gt;BOM&lt;/a&gt; (&lt;code&gt;window&lt;/code&gt; object) and other JS APIs.&lt;/p&gt;

&lt;p&gt;Each WebAssembly module has to declare a list of imported and exported symbols.&lt;/p&gt;

&lt;p&gt;Imported symbols usually used to communicate with a browser (or other host environment) and and are linked by a browser during WebAssembly module instantiation. WebAssembly module caller on JS side should pass those symbols as import object.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instantiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;wasm&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="nx"&gt;binary&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Exports are used to export module symbols (functions usually) to be executed from JavaScript.&lt;/p&gt;

&lt;p&gt;Unlike Go, &lt;a href="https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html" rel="noopener noreferrer"&gt;Emscripten&lt;/a&gt; or &lt;a href="https://rustwasm.github.io/wasm-bindgen/contributing/design/exporting-rust.html" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; provide convenient way to import or export symbols from our program. &lt;/p&gt;

&lt;p&gt;Go uses imports and exports for it's internal purposes and out of box doesn't provide a convenient way to link Go program functions to exports. I will cover this topic more in depth in next series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Sample
&lt;/h2&gt;

&lt;p&gt;The only way to communicate with JavaScript world is to interact with global namespace (&lt;code&gt;window&lt;/code&gt; or &lt;code&gt;globalThis&lt;/code&gt;), attaching or obtaining values from it using &lt;code&gt;syscall/js&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;Go provides way to wrap Go functions to JavaScript callable functions using &lt;code&gt;js.FuncOf&lt;/code&gt; and assigning them to JS objects (usually global object like &lt;code&gt;window&lt;/code&gt;) that can be used to call Go code from JS.&lt;/p&gt;

&lt;p&gt;Let's create a simple Go program that will export a simple greeter function that will print a simple "hello world" message.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"syscall/js"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;

    &lt;span class="c"&gt;// Wrap our Go function as JS function to make it callable.&lt;/span&gt;
    &lt;span class="n"&gt;jsFunc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FuncOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Assign our function to window.greeter&lt;/span&gt;
    &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Global&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"greeter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsFunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Prevent the program from exit&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// First argument is JS execution context (this)&lt;/span&gt;
&lt;span class="c"&gt;// and list of arguments passed to the function.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// The only way to return an error is to panic.&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Missing name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Program should be compiled for WebAssembly architecture with this command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;js &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wasm go build main.go


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Run a program
&lt;/h3&gt;

&lt;p&gt;Let's try to run this program. For demonstration purposes, I already created a snippet on Better Go Playground which allows executing Go programs in WASM environment - &lt;a href="https://goplay.tools/snippet/sNWAb8yhHtu" rel="noopener noreferrer"&gt;https://goplay.tools/snippet/sNWAb8yhHtu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Open the &lt;a href="https://goplay.tools/snippet/sNWAb8yhHtu" rel="noopener noreferrer"&gt;snippet&lt;/a&gt;, and select WebAssembly environment as shown on a screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa15llp41tpzfymzx7tiy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa15llp41tpzfymzx7tiy.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, click &lt;strong&gt;▶️ Run&lt;/strong&gt; button. Our program will notify that it's running and expecting &lt;code&gt;greeter&lt;/code&gt; function to be called.&lt;/p&gt;

&lt;p&gt;Open browser DevTools by pressing &lt;code&gt;F12&lt;/code&gt; key and go to &lt;strong&gt;Console&lt;/strong&gt; tab and call our &lt;code&gt;greeter&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fur021eu9d17ivhqgg641.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fur021eu9d17ivhqgg641.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you see, our function is working, but how this magic actually works?&lt;/p&gt;
&lt;h2&gt;
  
  
  JavaScript Invocation Internals
&lt;/h2&gt;

&lt;p&gt;To execute Go WebAssembly program, Go SDK provides a small glue-code file &lt;code&gt;wasm_exec.js&lt;/code&gt; which is located in &lt;code&gt;$GOROOT/misc/wasm/wasm_exec.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It provides a &lt;code&gt;window.Go&lt;/code&gt; object helper which will manage all JS-to-Go communication and will provide access to all JS objects. The &lt;code&gt;syscall/js.FuncOf&lt;/code&gt; function using this helper for wrapping Go functions and making them callable from JavaScript.&lt;/p&gt;
&lt;h3&gt;
  
  
  Exporting Go Function
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;js.FuncOf&lt;/code&gt; method wraps function to make it callable from JS. Wrapped function is put in specific internal map and have ID assigned to them. When function will be called from JS, wrapper will create an event with call information which will be routed to destination function by ID.&lt;/p&gt;

&lt;p&gt;All wrapped Go functions are registered in &lt;a href="https://github.com/golang/go/blob/master/src/syscall/js/func.go#L13" rel="noopener noreferrer"&gt;&lt;code&gt;funcs&lt;/code&gt;&lt;/a&gt; map inside of &lt;code&gt;syscall/js&lt;/code&gt; package. Map key is function ID which will be used to find target function.&lt;/p&gt;

&lt;p&gt;Here is a very abstract explanation of how &lt;a href="https://github.com/golang/go/blob/master/src/syscall/js/func.go#L41" rel="noopener noreferrer"&gt;&lt;code&gt;js.FuncOf&lt;/code&gt;&lt;/a&gt; is working:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;nextFuncID&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;

  &lt;span class="c"&gt;// Key-value pair of function ID and function&lt;/span&gt;
  &lt;span class="n"&gt;funcs&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;

  &lt;span class="c"&gt;// Pointer to `wasm_exec.js` Go object in Javascript&lt;/span&gt;
  &lt;span class="n"&gt;jsGo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Global&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;Go&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FuncOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;funcId&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;nextFuncID&lt;/span&gt;
  &lt;span class="n"&gt;nextFuncID&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;

  &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;funcs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;funcId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;
  &lt;span class="c"&gt;// Ask Go wasm_exec.js bridge to create a function wrapper using last inserted index&lt;/span&gt;
  &lt;span class="n"&gt;jsFuncWrapper&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jsGo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;_makeFuncWrapper&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;funcId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c"&gt;// js.Func is a wrapper between target Go function and JS mapping&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;funcId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;jsFuncWrapper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Go Function Wrapper Anatomy
&lt;/h3&gt;

&lt;p&gt;Let's take a look, how our exported &lt;code&gt;window.greeter&lt;/code&gt; function looks like. This function was created by &lt;code&gt;go._makeFuncWrapper&lt;/code&gt; method which was called by Go at previous step.&lt;/p&gt;

&lt;p&gt;JavaScript allows to get function's source code by calling &lt;code&gt;window.greeter.toString()&lt;/code&gt;.&lt;br&gt;
Here is how our greeter function is actually look like:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// Target Go function ID&lt;/span&gt;
    &lt;span class="na"&gt;this&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// JS function execution context&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;  &lt;span class="c1"&gt;// Passed arguments&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_pendingEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;
  &lt;span class="nx"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_resume&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you see, under the hood it will call Go &lt;code&gt;wasm_exec.js&lt;/code&gt; helper object, register an event with Go function and it's arguments and will resume a program to handle the request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Invoking Target Go Function
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv87fsc0rry7i0qkcmp6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv87fsc0rry7i0qkcmp6n.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When Go function wrapper is called from JavaScript, under the hood wrapper takes passed index of a function, creates an event with passed arguments and stores it in &lt;code&gt;go._pendingEvent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After that, it will “awake” Go by calling internal Go function &lt;code&gt;wasm_export_resume&lt;/code&gt; which is exported by every Go WebAssembly binary as &lt;code&gt;resume&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wasm_export_resume&lt;/code&gt; calls a global event handler function &lt;code&gt;handleEvent()&lt;/code&gt; which is located at &lt;code&gt;syscall/js&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;handleEvent&lt;/code&gt; reads target function ID and call arguments from &lt;code&gt;_pendingEvent&lt;/code&gt; property of JS Go object where we put all function call information inside Go function wrapper.&lt;/p&gt;

&lt;p&gt;Then, it gets target Go function from &lt;code&gt;funcs&lt;/code&gt; map, calls it and writes call result back to &lt;code&gt;go._pendingEvent.result&lt;/code&gt; property. Result of event is retuned by JS wrapper function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As this article shows, it's easy to export and call Go functions from JavaScript but Go function invocation process quite complex and requires context switching which is an expensive procedure. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://dev.to/x1unix/go-webassembly-internals-part-2-51am"&gt;next article&lt;/a&gt; will cover process of how Go communicates with JavaScript world under the hood and obtains values from JavaScript world.&lt;/p&gt;

</description>
      <category>go</category>
      <category>webassembly</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Improving Go playground</title>
      <dc:creator>Denis Sedchenko</dc:creator>
      <pubDate>Thu, 06 Aug 2020 13:54:11 +0000</pubDate>
      <link>https://dev.to/x1unix/improving-go-playground-236o</link>
      <guid>https://dev.to/x1unix/improving-go-playground-236o</guid>
      <description>&lt;p&gt;Greetings, Go devs.&lt;/p&gt;

&lt;p&gt;I often find myself thinking that I often encounter a situation when I need to do some small prototyping (playing with goroutines, etc) and Go's playground often is faster solution than a dedicated IDE window. Unfortunately play.golang.org is very primitive (goplay.space is better but not much), so I've decided to try to create something a bit better.&lt;/p&gt;

&lt;p&gt;A few months ago decided to try to create a better version of Go playground that will have a small valuable set of features that make prototyping comfortable enough, such as basic code autocomplete (stdlib only supported), syntax check, snippets and examples. Also as Go in WebAssembly trend starts to grow, WebAssembly support was added.&lt;/p&gt;

&lt;p&gt;In addition, user can customize the editor by enabling font ligatures, selecting editor font and some other small subset of options.&lt;/p&gt;

&lt;p&gt;I would like to know your opinion and get some feedback.&lt;/p&gt;

&lt;p&gt;URL: &lt;a href="https://goplay.tools/"&gt;https://goplay.tools/&lt;/a&gt;&lt;br&gt;
Source Code - &lt;a href="https://github.com/x1unix/go-playground"&gt;https://github.com/x1unix/go-playground&lt;/a&gt; (contribution is appreciated)&lt;/p&gt;

</description>
      <category>go</category>
      <category>discuss</category>
      <category>contributorswanted</category>
      <category>news</category>
    </item>
  </channel>
</rss>
